TransactionScope在一些机器上自动升级到MSDTC?
在我们的项目中,我们使用TransactionScope来确保我们的数据访问层在事务中执行它的操作。 我们的目标是不要求我们的terminal用户的机器上启用MSDTC服务。
麻烦的是,在我们的一半开发人员机器上,我们可以禁用MSDTC来运行。 另一半必须启用它或他们得到“MSDTC on [服务器]不可用”错误消息。
这真让我挠头,让我认真考虑回滚到基于ADO.NET事务对象的基于TransactionScope的解决scheme。 这看起来很疯狂 – 对我们开发人员的一半工作(而不是升级)相同的代码在其他开发人员的升级。
我希望有一个更好的答案跟踪为什么交易升级到DTC,但不幸的是它不。
下面是一些会导致麻烦的代码示例,在尝试升级的机器上,尝试在第二个连接上升级。打开()(是的,当时没有其他连接打开)。
using (TransactionScope transactionScope = new TransactionScope() { using (SqlConnection connection = new SqlConnection(_ConStr)) { using (SqlCommand command = connection.CreateCommand()) { // prep the command connection.Open(); using (SqlDataReader reader = command.ExecuteReader()) { // use the reader connection.Close(); } } } // Do other stuff here that may or may not involve enlisting // in the ambient transaction using (SqlConnection connection = new SqlConnection(_ConStr)) { using (SqlCommand command = connection.CreateCommand()) { // prep the command connection.Open(); // Throws "MSDTC on [SERVER] is unavailable" on some... // gets here on only half of the developer machines. } connection.Close(); } transactionScope.Complete(); }
我们真的在挖掘并试图弄清楚这一点。 以下是关于它的机器的一些信息:
- 开发1:Windows 7 x64 SQL2008
- 开发2:Windows 7 x86 SQL2008
- 开发3:Windows 7 x64
SQL2005SQL2008
开发人员不适用于:
- 开发4:Windows 7 x64,
SQL2008SQL2005 - 开发5:Windows Vista x86,SQL2005
- 开发6:Windows XP X86,SQL2005
- 我的家用电脑:Windows Vista Home Premium,x86,SQL2005
我应该补充一点,所有机器为了解决这个问题,都已经用Microsoft Update提供的所有东西进行了完整的修补。
更新1:
- http://social.msdn.microsoft.com/forums/en-US/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/描述了类似的问题…早在2006年!
- http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx – 阅读代码示例,它清楚地演示了一个嵌套的第二个连接(到第二个SQL服务器,实际上)将升级到DTC。 我们在代码中没有这样做 – 我们没有使用不同的SQL服务器,也没有使用不同的连接string,也没有嵌套的辅助连接打开 – 不应升级到DTC 。
- http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx (从2005年)谈到连接到SQL2000时总是如何升级到DTC。 我们正在使用SQL2005 / 2008
- http://msdn.microsoft.com/en-us/library/ms229978.aspx关于事务升级的MSDN。
MSDN事务升级页面指出以下情况会导致事务升级到DTC:
- 在交易中至less有一个不支持单阶段通知的持久资源。
- 交易中至less有两个支持单阶段通知的持久资源。 例如,征用单个连接不会导致事务被提升。 但是,每当您打开数据库的第二个连接导致数据库登记时,System.Transactions基础结构就会检测到它是事务中的第二个持久资源,并将其升级为MSDTC事务。
- 调用将该事务“编组”到不同的应用程序域或不同进程的请求。 例如,跨应用程序域边界的事务对象的序列化。 事务对象是按值封送的,这意味着任何跨越应用程序域边界(即使在同一个进程中)的尝试都会导致事务对象的序列化。 您可以通过以事务作为参数的远程方法调用来传递事务对象,也可以尝试访问远程事务服务组件。 这将对事务对象进行序列化,并导致升级,就像跨应用程序域序列化事务一样。 它正在分发,当地的交易pipe理者已经不够了。
我们没有遇到#3。 #2没有发生,因为一次只有一个连接,而且也是一个“持久资源”。 有什么方法可以发生#1? 一些导致它不支持单阶段通知的SQL2005 / 8configuration?
更新2:
个人重新调查,大家的SQL Server版本 – “Dev 3”实际上有SQL2008,“Dev 4”实际上是SQL2005。 那会教会我不要再相信我的同事。 ;)由于数据的这种变化,我敢肯定,我们发现我们的问题。 我们的SQL2008开发人员没有遇到这个问题,因为SQL2008有大量的真棒包括,SQL2005没有。
它也告诉我,因为我们要支持SQL2005,我们不能像以前一样使用TransactionScope,如果我们想要使用TransactionScope,我们需要传递一个单独的SqlConnection对象。这在SqlConnection无法轻易传递的情况下似乎有问题…它只是全局-SqlConnection实例的气味。 座位!
更新3
就这个问题澄清一下:
SQL2008:
- 在单个TransactionScope中允许多个连接(如上面的示例代码所示)。
- 注意事项#1:如果这些多个SqlConnections是嵌套的,即同时打开两个或多个SqlConnections,则TransactionScope将立即升级到DTC。
- 注意事项#2:如果一个额外的SqlConnection打开到一个不同的“持久资源” (即:一个不同的SQL Server),它会立即升级到DTC
SQL2005:
- 不允许单个TransactionScope中的多个连接。 如果打开第二个SqlConnection,则会升级。
更新4
为了使这个问题变得更加有用,而且为了更清楚起见,下面介绍如何使用单个 SqlConnection
将SQL2005升级到DTC:
using (TransactionScope transactionScope = new TransactionScope()) { using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); connection.Close(); connection.Open(); // escalates to DTC } }
这似乎打破了我,但我想我可以理解,如果每个调用SqlConnection.Open()
从连接池抓取。
“但是为什么会这样呢?” 那么,如果你在打开这个连接之前使用了一个SqlTableAdapter,SqlTableAdapter将会打开和closures这个连接,从而有效地完成你的事务,因为你现在不能重新打开它。
所以,基本上,为了成功地使用SQL2005的TransactionScope,你需要有一些全局的连接对象,从第一个TransactionScope的实例化开始,直到不再需要为止。 除了全局连接对象的代码味之外,首先打开连接并closures最后一个连接对象与尽快打开连接并尽快closures它的逻辑是不一致的。
SQL2008可以在一个TransactionScope
使用多个SQLConnection
,只要连接没有同时打开,这将导致多个“物理”TCP连接,因此需要升级。
我看到你的一些开发人员有SQL2005和其他SQL2008。 你确定你已经正确识别哪些正在升级,哪些不是?
最明显的解释是,使用SQL 2008的开发人员不会升级。
我对这个话题的研究结果:
请参阅避免不必要地升级到分布式事务
我仍在调查Oracle的升级行为: 跨多个连接到同一个数据库的事务是否升级到DTC?
连接到2005年时,该代码将导致升级。
检查MSDN上的文档 – http://msdn.microsoft.com/en-us/library/ms172070.aspx
SQL Server 2008中的可促进事务
在.NET Framework和SQL Server 2005的2.0版本中,在TransactionScope中打开第二个连接会自动将事务提升为完整的分布式事务,即使两个连接都使用相同的连接string。 在这种情况下,分布式事务会增加不必要的开销,从而降低性能
从SQL Server 2008和.NET Framework 3.5版开始,如果在closures上一个事务之后在事务中打开了另一个连接,则本地事务将不再提升为分布式事务。 如果您已经在使用连接池并参与事务处理,则不需要更改代码。
我无法解释为什么Dev 3:Windows 7 x64,SQL2005成功,Dev 4:Windows 7 x64失败。 你确定这不是相反吗?
我不知道为什么这个答案被删除,但这似乎有一些相关的信息。
回答8年4月4日在17:42 爱德华多
-
在连接string上设置Enlist = false以避免事务自动登记。
-
手动征集连接作为交易范围的参与者 。 [ 原文陈旧]还是这样做: 如何防止自动MSDTC升级 [archive.is]
我不太确定嵌套连接是否是问题。 我正在调用SQL服务器的本地实例,它不会生成DTC?
public void DoWork2() { using (TransactionScope ts2 = new TransactionScope()) { using (SqlConnection conn1 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;")) { SqlCommand cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())"); cmd.Connection = conn1; cmd.Connection.Open(); cmd.ExecuteNonQuery(); using (SqlConnection conn2 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;Connection Timeout=100")) { cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())"); cmd.Connection = conn2; cmd.Connection.Open(); cmd.ExecuteNonQuery(); } } ts2.Complete(); } }
如果你在里面使用多于1个连接的话,TransactionScope总是升级到DTC事务。 上面的代码可以在禁用DTC的情况下唯一的方法是,如果有很大的可能性,那么两次都会从连接池获得相同的连接。
“麻烦的是,在我们的一半开发人员机器上,我们可以禁用MSDTC来运行。” 你确定它被禁用了吗?)