TransactionScope是否在某些计算机上自动升级为MSDTC?


284

在我们的项目中,我们使用TransactionScope来确保我们的数据访问层在事务中执行其动作。我们的目标是要求在最终用户的计算机上启用MSDTC服务。

麻烦的是,在我们一半的开发人员机器上,我们可以在禁用MSDTC的情况下运行。另一半必须启用它,否则他们会收到“ [SERVER]上MSDTC不可用”错误消息。

这真的让我抓狂了,还让我认真考虑回滚到基于ADO.NET事务对象的类似HomeScope的TransactionScope解决方案。这似乎是疯狂的-在我们一半的开发人员的代码上起作用(并且不会升级)的相同代码确实会在其他开发人员的代码升级。

我希望对Trace有了一个更好的答案,为什么交易会升级为DTC,但不幸的是却没有。

这是会引起问题的示例代码,在尝试升级的机器上,它尝试在第二个连接上升级.Open()(是的,当时没有其他连接打开)。

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 SQL2005 SQL2008

开发人员无法使用:

  • 版本4:Windows 7 x64,SQL2008 SQL2005
  • 版本5:Windows Vista x86,SQL2005
  • 版本6:Windows XP X86,SQL2005
  • 我的家用电脑:Windows Vista Home Premium,x86,SQL2005

我应该补充说,所有计算机都已使用Microsoft Update的所有功能进行了全面修补,以解决问题。

更新1:

该MSDN事务升级页面指出以下情况将导致事务升级到DTC:

  1. 事务中至少有一个不支持单阶段通知的持久资源。
  2. 事务中至少有两个支持单阶段通知的持久资源。例如,仅注册一个连接不会导致事务升级。但是,每当您打开与数据库的第二个连接而导致该数据库加入时,System.Transactions基础结构都会检测到它是事务中的第二个持久资源,并将其升级为MSDTC事务。
  3. 调用将事务“封送”到另一个应用程序域或另一个进程的请求。例如,跨应用程序域边界对事务对象进行序列化。事务对象按值封送处理,这意味着任何试图跨应用程序域边界传递该对象(即使在同一过程中)都会导致该事务对象的序列化。您可以通过调用将事务作为参数的远程方法来传递事务对象,也可以尝试访问远程事务服务组件。这会序列化事务对象,并导致升级,就像跨应用程序域序列化事务时一样。它正在分发,并且本地事务管理器不再足够。

我们没有遇到#3。#2不会发生,因为一次只有一个连接,而且也连接到一个“持久资源”。有什么办法可能发生#1?是否导致某些SQL2005 / 8配置不支持单阶段通知?

更新2:

重新调查后,个人而言,每个人的SQL Server版本-“ Dev 3”实际上具有SQL2008,而“ Dev 4”实际上是SQL2005。那将教会我永远不要再信任我的同事。;)由于数据的这种变化,我很确定我们已经找到了问题。我们的SQL2008开发人员没有遇到此问题,因为SQL2008包括SQL2005没有的大量功能。

它还告诉我,因为我们将要支持SQL2005,所以我们不能像以前那样使用TransactionScope,并且如果我们想使用TransactionScope,我们将需要传递单个SqlConnection对象。在无法轻松传递SqlConnection的情况下,这似乎是有问题的...它只是闻到global-SqlConnection实例的味道。座位!

更新3

只是为了在这里澄清问题:

SQL2008:

  • 允许在单个TransactionScope中进行多个连接(如上面的示例代码所示)。
  • 注意事项1:如果嵌套了多个SqlConnection,即同时打开两个或多个SqlConnection,则TransactionScope将立即升级为DTC。
  • 警告#2:如果向其他“持久资源”(即:其他SQL Server)打开了额外的SqlConnection ,它将立即升级为DTC

SQL2005:

  • 在单个TransactionScope期间内不允许多个连接。当/如果打开第二个SqlConnection,它将升级。

更新4

在做出这个问题更加的利益乱七八糟的有用的,只是为了更清晰起见,这里是你如何能得到SQL2005升级到DTC带有 SqlConnection

using (TransactionScope transactionScope = new TransactionScope()) {
   using (SqlConnection connection = new SqlConnection(connectionString)) {
      connection.Open();
      connection.Close();
      connection.Open(); // escalates to DTC
   }
}

这对我来说似乎很困难,但是我想我可以理解是否每个调用SqlConnection.Open()都从连接池中获取。

“为什么会这样呢?” 好吧,如果在打开该连接之前对它使用SqlTableAdapter,则SqlTableAdapter将打开和关闭该连接,为您有效地完成了事务,因为您现在无法重新打开它。

因此,基本上,为了成功地将TransactionScope与SQL2005结合使用,您需要具有某种全局连接对象,该对象从实例化第一个TransactionScope直到不再需要它时都保持打开状态。除了全局连接对象的代码气味外,首先打开连接并最后关闭它与延迟打开连接并尽快关闭连接的逻辑是矛盾的。


您是否可以展开“在此处执行可能涉及或不涉及环境事务的其他事情”。当然,其中的内容会极大地影响代码的行为吗?
RichardOD

2
“#2不会发生,因为一次只有一个连接”-#2并没有说第二个连接需要同时打开,而只是需要在同一事务中加入它。

3
非常感谢您通过Update 4进行报告,该更新显示了仅使用一个SqlConnection如何发生升级。尽管确实确保仅使用单个SqlConnection,但这正是我所遇到的问题。很高兴知道这台计算机是疯狂的,而不是我。:-)
奥兰·丹尼森

在连接池方面,如果我们有多个连接(并且在必要时嵌套),并且一次要打开和关闭一个,那么我们将使用1个真正的连接池资源或每个连接1个资源,我试图对其进行合理化,以确定是否具有适当范围的“应征”式入场券(我想避免)
brumScouse

1
同一事务作用域下的嵌套连接将升级为分布式事务。从SQL Server 2008及更高版本开始,同一事务范围内的多个(非嵌套)连接将不会升级为分布式事务。
PreguntonCojoneroCabrón

Answers:


71

如果没有同时打开连接,则SQL Server 2008可以SQLConnectionTransactionScope不升级的情况下使用多个s ,前提是连接不能同时打开,这将导致多个“物理” TCP连接,因此需要升级。

我看到您的某些开发人员使用SQL Server 2005,其他开发人员使用SQL Server2008。您确定已正确确定哪些正在升级,哪些没有在升级?

最明显的解释是,使用SQL Server 2008的开发人员没有升级。


是的,细节是正确的,有人在看代码吗?在事务范围内有两个连接,但是,只有一个连接在一个时刻被实例化和打开。另外,不,DTC在正在运行的计算机上未运行。
Yoopergeek,2009年

1
“但是,只有一个连接实例在同一时刻被实例化并打开了”-为什么如此重要?使用SQL2005,如果在一个事务范围内打开多个连接,则将升级它们是否同时保持打开状态。如果考虑一下,这是合乎逻辑的。

您和我们的专家现在对我进行第二次猜测,我急于周一开始工作,并仔细检查他们的单台计算机,并确保SQL Server版本与以前的报告相同。
Yoopergeek

19
你和胡维斯都是对的。我满脸蛋。谢谢您用线索棒打我。:)因为您是第一位的,所以您得到了答案。我想补充一点,不过-SQL2008允许打开多个连接,但不能同时打开。在任何给定时间仍然只能打开一个连接,否则TransactionScope将升级为DTC。
Yoopergeek,2009年

@Yoopergeek我可以验证您的“不同时”很重要,并相应地编辑@Joe的答案。在测试期间监视TCP连接表明,当不同时使用连接时,旧的TCP连接将被重用,因此TransactionScope可以COMMIT在服务器端使用单个连接,这将使升级变得多余。
尤金·别列索夫斯基

58

我对该主题的研究结果是:

在此处输入图片说明

请参阅避免不必要地升级到分布式事务

我仍在调查Oracle的升级行为: 跨多个连接到同一数据库的事务是否升级为DTC?


1
感谢您分享研究成果。真的有帮助。另一种快速查询。TransactionScope()和sqlConnection.BeginTransaction()之间有什么区别?
Baig

根据此功能请求,ODAC 12C现在应表现为SQL 2008,在使用到同一数据源的连续连接时不升级为分布式。
弗雷德里克

31

连接到2005时,该代码导致升级。

查看有关MSDN的文档-http: //msdn.microsoft.com/zh-cn/library/ms172070.aspx

SQL Server 2008中的可升级事务

在.NET Framework和SQL Server 2005的2.0版中,即使在两个连接都使用相同的连接字符串的情况下,在TransactionScope中打开第二个连接也会自动将事务升级为完全分布式的事务。在这种情况下,分布式事务会增加不必要的开销,从而降低性能。

从SQL Server 2008和.NET Framework 3.5版开始,如果在关闭前一个事务后在事务中打开了另一个连接,则本地事务将不再升级为分布式事务。如果您已经在使用事务连接池和登记,则无需更改代码。

我无法解释为什么开发3:Windows 7 x64,SQL2005成功和开发4:Windows 7 x64失败。您确定不是相反吗?



2

我不太确定嵌套连接是否是问题所在。我正在调用SQL Server的本地实例,它不会生成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();
        }
    }

您正在使用哪个版本的SQL Server?我想知道@Peter Meinl的答案是否需要更新以反映2008R2和/或Denali中所做的任何更改。
Yoopergeek'5

我正在使用SQL Server 2008 R2。
Iftikhar Ali 2012年

我想知道2008 R2是否表现更好?@hwiechers的回答也让我想知道您要编译的Framework版本是否在阻止升级。最后,我想知道它是否是本地R2实例是否有任何区别。我希望我有时间/资源来研究随着2008 R2和SQL Server 2012的发布而发生的变化
。– Yoopergeek

不确定嵌套连接是否是问题?大声笑...那么好把它删除!,为什么人们在并非绝对必要时使用语句嵌套,我永远不会知道。
Paul Zahra

1

如果您使用内部访问多个连接,则TransactionScope始终升级为DTC事务。上面的代码可以在禁用DTC的情况下运行的唯一方法是,如果您很有可能两次都从连接池中获得相同的连接。

“问题是,在我们一半的开发人员机器上,我们可以禁用MSDTC来运行。” 确定要禁用它吗?)


0

确保您的connectionString没有将池设置为false。这将为TransactionScope中的每个新SqlConnection产生一个新连接,并将其升级为DTC。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.