• ADO.NET中的TransactionScope何时需要启用MSTDC(分布式事务管理)


    我们知道在ADO.NET中可以用TransactionScope来将多个SqlConnection(多个数据库连接)执行的Sql语句放入一个事物中提交或取消,但是使用TransactionScope的时候也要额外小心,因为TransactionScope在特殊情况下需要启动MSDTC(分布式事务管理)服务,那么我们来看看什么时候TransactionScope需要启动MSDTC呢?

    首先来声明下本例中代码和数据库的环境,首先本例使用的数据库是SqlServer 2008 R2,本例中C#代码运行的电脑和SqlServer数据库所在的电脑是局域网中的两个机器,也就是说ADO.NET所在的程序和SqlServer是在两台电脑上,为什么要强调这个问题呢?因为我查阅文章发现ADO.NET所在的程序和SqlServer部署在一台电脑,和部署在两台电脑上,TransactionScope的行为还不太一样,这个也和SqlServer的版本有关系,后面会有总结。

    我们先来看一个例子,在下面的ADO.NET客户端程序代码中我们使用TransactionScope启动了两个SqlConnection,这两个SqlConnection使用的连接字符串都相同,而此时我们的ADO.NET客户端程序所在的电脑是没有启动MSDTC的,所以如果TransactionScope需要启动MSDTC那么下面这段代码就会抛出异常。

     1 protected const string connectionString = @"Data Source=192.168.1.3;Initial Catalog=Mobile_Reporting_DM_Staging;Persist Security Info=True;User ID=sa;Password=0okm9ijn*UHB&YGV";
     2 
     3 static void Main(string[] args)
     4 {
     5     string sql = string.Empty;
     6     SqlCommand sqlCom;
     7 
     8     using (TransactionScope tran = new TransactionScope(TransactionScopeOption.RequiresNew))
     9     {
    10         using (SqlConnection sqlCon1 = new SqlConnection(connectionString))
    11         {
    12             sqlCon1.Open();
    13 
    14             sql = "insert into [dbo].[T_People](Name,age) values(N'王刚',20)";
    15 
    16             sqlCom = new SqlCommand(sql, sqlCon1);
    17             sqlCom.ExecuteNonQuery();
    18         }
    19 
    20         using (SqlConnection sqlCon2 = new SqlConnection(connectionString))
    21         {
    22             sqlCon2.Open();
    23 
    24             sql = "insert into [dbo].[T_People](Name,age) values(N'李强',30)";
    25 
    26             sqlCom = new SqlCommand(sql, sqlCon2);
    27             sqlCom.ExecuteNonQuery();
    28         }
    29 
    30         tran.Complete();
    31     }
    32 
    33     Console.ReadLine();
    34 }

    而执行上面这段代码后我们发现程序并没有出现异常,而且两个SqlConnection都成功地向数据库中插入了一条数据,这说明上面这段代码TransactionScope并没有用到MSDTC服务。这说明如果TransactionScope中启动的多个SqlConnection(多个数据库连接)连接的同一个SqlServer实例的相同数据库,那么TransactionScope是不会用到MSDTC服务的。

    然后我们将上面的代码做一下修改,定义两个连接字符串connectionString1和connectionString2,分别指向同一个SqlServer实例的不同数据库,其中connectionString1指向了数据库Mobile_Reporting_DM_Staging,而connectionString2指向了数据库Mobile_Reporting_DM,那么这一次TransactionScope中的两个SqlConnection会分别连接同一个SqlServer实例的不同数据库,我们执行下面的代码看会发生什么呢?

     1 protected const string connectionString1 = @"Data Source=192.168.1.3;Initial Catalog=Mobile_Reporting_DM_Staging;Persist Security Info=True;User ID=sa;Password=0okm9ijn*UHB&YGV";
     2 protected const string connectionString2 = @"Data Source=192.168.1.3;Initial Catalog=Mobile_Reporting_DM;Persist Security Info=True;User ID=sa;Password=0okm9ijn*UHB&YGV";
     3 
     4 static void Main(string[] args)
     5 {
     6     string sql = string.Empty;
     7     SqlCommand sqlCom;
     8 
     9     using (TransactionScope tran = new TransactionScope(TransactionScopeOption.RequiresNew))
    10     {
    11         using (SqlConnection sqlCon1 = new SqlConnection(connectionString1))
    12         {
    13             sqlCon1.Open();
    14 
    15             sql = "insert into [dbo].[T_People](Name,age) values(N'王刚',20)";
    16 
    17             sqlCom = new SqlCommand(sql, sqlCon1);
    18             sqlCom.ExecuteNonQuery();
    19         }
    20 
    21         using (SqlConnection sqlCon2 = new SqlConnection(connectionString2))
    22         {
    23             sqlCon2.Open();
    24 
    25             sql = "insert into [dbo].[T_People](Name,age) values(N'李强',30)";
    26 
    27             sqlCom = new SqlCommand(sql, sqlCon2);
    28             sqlCom.ExecuteNonQuery();
    29         }
    30 
    31         tran.Complete();
    32     }
    33 
    34     Console.ReadLine();
    35 }

    执行上面代码后我们发现在代码第23行sqlCon2.Open()抛出了异常,异常提示"该伙伴事务管理器已经禁止了它对远程/网络事务的支持。 (异常来自 HRESULT:0x8004D025)",这句话的意思就是TransactionScope需要用到MSDTC(分布式事务管理)服务,但是操作系统的MSDTC服务并没有启动。

    所以我们看到当我们要求TransactionScope中启动的多个SqlConnection(多个数据库连接)连接同一个SqlServer实例的不同数据库时,TransactionScope就需要启动MSDTC(分布式事务管理)服务了。这与我预先的理解有很大的不同,我以前一直以为只有当TransactionScope中的多个SqlConnection(多个数据库连接)连接不同的SqlServer实例时才需要MSDTC,但是事实证明当TransactionScope中的SqlConnection连接同一个SqlServer实例的不同数据库时就需要MSDTC了。

    下面我们来总结下TransactionScope什么时候需要启动MSDTC(以下结论都是基于SqlServer2008及其后续版本的,SqlServer2005在后面会有特殊说明)

    1. 不管你ADO.NET程序所在的服务器和数据库服务器是同一台电脑或不同的电脑,只要TransactionScope中的多个SqlConnection(多个数据库连接)连接的是同一个SqlServer实例的相同数据库时,TransactionScope不需要MSDTC(分布式事务管理)服务
    2. 不管你ADO.NET程序所在的服务器和数据库服务器是同一台电脑或不同的电脑,只要TransactionScope中有多个SqlConnection(多个数据库连接)连接同一个SqlServer实例的不同数据库时,TransactionScope就需要启用MSDTC(分布式事务管理)服务
    3. 不管你ADO.NET程序所在的服务器和数据库服务器是同一台电脑或不同的电脑,只要TransactionScope中的多个SqlConnection(多个数据库连接)连接不同的SqlServer实例时,TransactionScope需要启用MSDTC(分布式事务管理)服务
    4. 如果你ADO.NET程序所在的服务器和数据库服务器是同一台电脑,TransactionScope将不支持环回链接服务器,因为SqlServer的分布式事务本来就不支持环回链接服务器,环回链接服务器的解释请看这里
    5. 不管你ADO.NET程序所在的服务器和数据库服务器是同一台电脑或不同的电脑,只要TransactionScope中有SqlConnection(数据库连接)执行的Sql语句使用到了LinkedServer(链接服务器),那么TransactionScope就需要启用MSDTC(分布式事务管理)服务,不光是TransactionScope,实际上只要SqlServer的事务中用到了LinkedServer(链接服务器),那么SqlServer就会启动分布式事务从而用到MSDTC服务

    所以我们可以看到在不同的应用场景下TransactionScope的行为是不一样的。另外关于上面第二点,其实可以使用变通的方法来避免TransactionScope启用MSDTC,如果数据库A和数据库B位于同一个SqlServer实例,那么我们可以让TransactionScope中的多个SqlConnection(多个数据库连接)都连接到数据库A,但是在Sql语句中使用[数据库B].[Schema].[表名]这种带数据库前缀的完全限定名来操作数据库B的数据,这样TransactionScope是不会用到MSDTC的。

    这里要特别强调下SqlServer2005及其之前的版本,实际上SqlServer2005及其之前的版本对TransactionScope的支持力度非常差,我们发现数据库是SqlServer2005时,只要ADO.NET程序所在的服务器和数据库服务器不是同一台电脑,TransactionScope就需要启用MSDTC(分布式事务管理)服务,即便在TransactionScope中只使用了一个SqlConnection发起唯一的一个数据库连接,TransactionScope都要求启用MSDTC。所以当数据库是SqlServer2005时,只有当ADO.NET程序所在的服务器和数据库服务器是同一台电脑,并且TransactionScope中的多个SqlConnection(多个数据库连接)连接的是同一个SqlServer实例的相同数据库时,TransactionScope才不需要MSDTC(分布式事务管理)服务。

    此外关于MSDTC(分布式事务管理)服务在这里多说一句,分布式事务要求在ADO.NET客户端程序所在的服务器和数据库服务器上同时启用MSDTC才能正常工作,而单方面只启动ADO.NET客户端程序所在的服务器上的MSDTC或只启动数据库服务器上的MSDTC是不行的。因为分布式事务实际上是通过不同服务器间的MSDTC服务来提交的,所以涉及到分布式事务的所有服务器都必须要启用MSDTC服务后,分布式事务才可以正常工作。

    另外本文的很多观点借鉴了下面这篇博客的内容,大家可以参考下,也感谢这位博主的对TransactionScope的研究和分享。

    文章链接

  • 相关阅读:
    IDEA常用快捷键和常用插件集成,持续更新......
    【转】javascript日期操作详解(脚本之家整理)
    学习AngularJs:Directive指令用法
    Angular过滤器 自定义及使用方法
    【转】AngularJS 日期格式化 字典
    【转】AngularJs $location获取url参数
    【转】JavaScript对Json节点的增删改
    Asp.Net Mvc后台数据验证自测小Demo
    你必须先行上路
    【转】俞敏洪:我和马云差了8个字,结果财富相差2200亿美金
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/5814026.html
Copyright © 2020-2023  润新知