TransactionScope会在某些机器上自动升级到MSDTC的吗?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (6)
  • 关注 (0)
  • 查看 (200)

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

问题是,在我们一半的开发人员机器上,我们可以在禁用MSDTC的情况下运行。另一半必须启用它,否则他们将获得“MSDTC on服务器不可用“错误信息。

跟踪事务升级到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 SQL 2008
  • 开发2:Windows 7 x86 SQL 2008
  • 开发3:Windows 7 x64SQL 2005SQL 2008

不针对开发人员:

  • 开发4:Windows 7 x64,SQL 2008SQL 2005
  • 开发5:WindowsVista x86,SQL 2005
  • 开发6:Windows XP X86,SQL 2005
  • My Home PC:WindowsVista家庭高级版,x86,SQL 2005

所有机器为了寻找问题都已经完全用Microsoft Update提供的所有软件进行了修补。

MSDN事务升级页面声明,以下条件将导致事务升级到DTC:

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

我重新调查了每个人的SQL Server版本 - “Dev 3”实际上有SQL2008,“Dev 4”实际上是SQL2005。 由于数据的这种变化,我已经确定找到了问题。 我们的SQL2008开发人员没有遇到这个问题,因为SQL2008包含SQL2005所没有的功能。

由于我们支持SQL 2005,所以不能像以前那样使用TransactionScope。如果我们想要使用TransactionScope,我们需要在周围传递一个SqlConnection对象...这在SqlConnection很难被传递的情况下是有问题的。它只是看起来像是全局-SqlConnection实例。

SQL 2008:

  • 允许在单个TransactionScope中进行多个连接(如上面的示例代码所示)。
  • 警告1:如果这些多个SqlConnections是嵌套的,也就是说,同时打开两个或多个SqlConnections,TransactionScope将立即升级到DTC。
  • 警告2:如果另一个SqlConnection打开到不同的“持久资源”(即:不同的SQL Server,),它会立即升级到DTC

SQL 2005:

  • 不允许单个TransactionScope中的多个连接。 当/打开第二个SqlConnection时,它会升级。

通过使用单个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将打开并关闭连接,有效地为你完成事务。

因此,基本上,为了成功地使用SQL2005的TransactionScope,你需要有某种全局连接对象,从第一个TransactionScope的点一直保持开放,直到不再需要它为止。除了一个全局连接对象的代码,首先打开连接并关闭它,这与尽可能晚打开连接的逻辑以及尽快关闭连接的逻辑是不一致的。

提问于
用户回答回答于

SQL2008可以在TransactionScope中使用多个SQLConnections,而不需要升级,但是如果同时打开多个连接的话会导致多个“物理”TCP连接,因此需要升级。

有一些开发人员使用SQL 2005,而其他开发人员则使用SQL 2008。

关于这个问题最明显的解释是,使用SQL 2008的开发人员没有升级。

用户回答回答于

TransactionScope总是升级到DTC事务,如果你在内部使用了超过1个连接,以上代码可以DTC禁用从连接池中获取两次相同的链接。

“问题是,在我们一半的开发人员机器上,我们可以在禁用MSDTC的情况下运行”。

用户回答回答于

我调用了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();
        }
    }
用户回答回答于

http://msdn.microsoft.com/en-us/Library/ms 172070.aspx

SQLServer 2008中的可提升事务

在.NET Framework和SQL Server 2005的2.0版中,在TransactionScope中打开第二个连接会自动将事务提升为完整的分布式事务,即使两个连接都使用相同的连接字符串。 在这种情况下,分布式事务会增加不必要的开销,从而降低性能。 从SQL Server 2008和.NET Framework 3.5版开始,如果在关闭上一个事务后在事务中打开了另一个连接,则本地事务将不再提升为分布式事务。 如果你已经在使用连接池并参与事务处理,则不需要更改代码。

但是我没有办法解释为什么Dev 3:Windows 7 x64、SQL 2005成功和Dev 4:Windows 7 x64失败。

扫码关注云+社区

领取腾讯云代金券