如何避免误用分布式事务(System.Transactions.TransactionScope)

如下内容来源与:http://www.cyqdata.com/cyq1162/article-detail-54453数据库

 

1:本地事务DbTransaction和分布式事务TransactionScope的区别:

1.1:System.Data.Common.DbTransaction:

本地事务:这个没什么好说了,就是单个事务,每种数据库都有本身的实现,事务的深度内涵能够搜索查看相关的文章,不是本文介绍的重点。架构

1.2:System.Transactions.TransactionScope:

分布式事务,须要添加引用System.Transactions,同时启用MSDTC分布式事务服务:一般使用方式为:框架

 using (System.Transactions.TransactionScope ts = new System.Transactions.TransactionScope())
 {
                //代码块A
                //代码块B
                ts.Complete();//提交事务
 }

分布式事务本质上是引入了第三方裁判,来想办法对多个本地事务监控同时成功或同时失败,这里分享几个知识点:分布式

A:若是代码块里,若存在两个或以上数据库连接DbConnection,则须要启动微软的MSDTC分布式事务服务。工具

 

用命令行启动或中止服务:测试

 

B:若是代码块里,只有一个数据库连接DbConnection,那么实际上只是本地事务在处理,就算MSDTC分布式事务服务没启动,也不会报错。优化

C:对于TransactionScope包含的代码块,本质是监控了代码块里数据库连接DbConnection的个数,若是有多个不一样的对象,则引入MSDTC当裁判,而实际执行的事务的仍是各个本地事务。ui

D:MSDTC老是不够稳定,我在测试时两个简单的事务一块儿时,按住F5不停刷新,居然能把MSSQL服务也给挂了,而用本地事务就算跨库也不会有问题。编码

 

因此,不是必须的状况,尽可能不要引入分布式事务,应该避免使用TransactionScope来包含事务块的冲动。 spa

 

2:能够用本地事务解决,避免使用分布式事务场景:

2.1:项目只有一个数据库,这个是最应该避免,多个表间的事务, 彻底是本地事务可处理的范围:

问题:一个代码块里N个实体类杂交操做,每一个实体类带有各类的数据库连接,从而引起了MSDTC。

能够:共用一个DbConnection对象,来避免启用MSDTC。

 

2.2:项目多个数据库,若是数据库间使用了相同的访问帐号和密码,这种状况也能够避免:

每种数据库有本身的解决方式:像MSSQL,跨库处理只要加前缀(dbname..tablename)就能够,所以也使的只使用本地事务就能够处理了。

 

3:回顾下我之前的项目场景:

3.1:从下图是我08年在中域的项目:有19个数据库,对于数据库连接而言,惟一的不一样只有数据库的名称:

 

进化:能不能只保留一条,其它的经过动态切换数据库名称来改连接字符串?

 

3.2:那时候,个人架构仍是很新手,经过CodeSmith生成了大量的代码文件:

实体类项目,工厂项目,接口项目,数据操做,还有大量的增删改查存储过程,只是为了基础的增删改查并且只针对MSSQL。

随便展开一个项目都会看到大量的文件夹,里面有大量的文件,而这些东东,都是代码生成器的杰做:

 

19个数据库啊,NN个表,光生成这些基本的增删改查,整个项目就好像高端大气上档次了。

进化:能不能消灭这些大量的文件,简单是咱们不断追求的目标。

 

3.3:这么多数据库,如何跨数据库事务?

对于生成的大量的实体,每一个表的操做都是一个新的连接,单库间的事务都必须MSDTC了,更别说19个库间的跨库事务了。

进化:能不能本地事务搞定这些,这是每一个ORM能够思考的方向。

 

4:CYQ.Data 提供的解决方案: 

为了消灭上述的那些大量的生成文件,我在后续新的公司写了传统的ORM框架:XQData(这个框架一直没露过面,也只是支持MSSQL,用反射消灭了好多层,只留下实体层)。

对于框架的演进,多数都来源于项目中遇到的问题,或复杂的场景,须要解决或者简化,才有了不断升级的可能,并非无中生有,所以,一个框架,若是不能不断在在项目中实战,那么不少问题和细节等可能都没法发现,更新也会遥遥无期。 

而CYQ.Data的不断升级,说明我一直在奋战在一线的编码生涯中和网友各大项目中的实战反馈中。 

 

下面针对最近的优化调整,演示下CYQ.Data是怎么解决上面提到的几个问题:

4.1:多个数据库的数据库连接切换:

A:先用配置工具生成针对多数据库的枚举文件,和成demo和test两个数据库:

两个数据库就生成两次了。

B:配置一个默认的连接字符串,到Demo数据库:

<add name="Conn" connectionString="server=.;database=demo;uid=sa;pwd=123456"/>

C:打印操做Test数据库表的连接字符串:

using (MAction action = new MAction(TestEnum.Users))
            {
                Console.WriteLine(action.ConnectionString);
            }

输出:

 

这里自动切换了数据为名称为新的连接,而我动手脚的地方就是在枚举的名称了。

4.2:本地多数据库跨事务,示例:

            AppDebug.OpenDebugInfo = true;
            AppDebug.Start();
            using (MAction action = new MAction(DemoEnum.Users))
            {
                 action.BeginTransation();//开启事务
                 action.Fill(12);
                Console.WriteLine(action.ConnectionString);//打印数据库连接
                action.ResetTable(TestEnum.Users);//切换数据库
                Console.WriteLine(action.ConnectionString);//打印数据库连接
                action.Fill(12);
                action.EndTransation();
            }
            Console.WriteLine(AppDebug.Info);//输出全部执行的SQL语句。
            AppDebug.Stop();

输出的结果:

 

说明:在事务中,当检测到事务开启时,为了使用本地事务,并无切换数据库,而是采用了前缀来执行操做。

 

 

若是注释掉代码中的事务,则是直接切换数据库连接,输出会以下图:

 

说明:若是没有开启事务,则直接切换了数据库连接,并消消灭前缀语法。

总结:

对于.NET领域,微软除了提供这个不太稳定的MSDTC,彷佛没有发现其它分布式事务的解决方案,好在通常的项目,咱们在本地事务就能够处理。

但愿微软能够在一些重点分布式上多作点研究、普及和推广,提供大型项目的解决方案,别成天操碎心在那些简单的增删改查上。

相关文章
相关标签/搜索