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在后面会有特殊说明)
- 不管你ADO.NET程序所在的服务器和数据库服务器是同一台电脑或不同的电脑,只要TransactionScope中的多个SqlConnection(多个数据库连接)连接的是同一个SqlServer实例的相同数据库时,TransactionScope不需要MSDTC(分布式事务管理)服务
- 不管你ADO.NET程序所在的服务器和数据库服务器是同一台电脑或不同的电脑,只要TransactionScope中有多个SqlConnection(多个数据库连接)连接同一个SqlServer实例的不同数据库时,TransactionScope就需要启用MSDTC(分布式事务管理)服务
- 不管你ADO.NET程序所在的服务器和数据库服务器是同一台电脑或不同的电脑,只要TransactionScope中的多个SqlConnection(多个数据库连接)连接不同的SqlServer实例时,TransactionScope需要启用MSDTC(分布式事务管理)服务
- 如果你ADO.NET程序所在的服务器和数据库服务器是同一台电脑,TransactionScope将不支持环回链接服务器,因为SqlServer的分布式事务本来就不支持环回链接服务器,环回链接服务器的解释请看这里。
- 不管你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的研究和分享。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2015-08-27 Reporting Service报表项默认可见+号和-号的显示问题