代码之家  ›  专栏  ›  技术社区  ›  Yoopergeek

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

  •  276
  • Yoopergeek  · 技术社区  · 15 年前

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

    问题是,在我们一半的开发人员机器上,我们可以在禁用msdtc的情况下运行。另一半必须启用它,否则他们会 “在[server]上的msdtc不可用” 错误信息。

    这真的让我抓耳挠腮,让我认真考虑回到一个基于ADO.NET事务对象的自选TransactionScope类解决方案。这似乎是疯狂的-相同的代码在我们开发人员的一半的 升级其他开发人员的。

    我希望有更好的答案 Trace why a transaction is escalated to DTC 但不幸的是,事实并非如此。

    下面是一个将导致故障的代码示例,在尝试升级的计算机上,它尝试在第二个connection.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
    • dev 6:Windows XP x86,sql2005
    • 我的家用电脑:Windows Vista Home Premium、x86、SQL2005

    我应该补充一点,为了解决这个问题,所有的机器都已经完全修复了Microsoft Update提供的所有功能。

    更新1:

    “msdn事务升级”页说明以下情况将导致事务升级到DTC:

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

    我们没有体验到3。#2不会发生,因为一次只有一个连接,而且它也是一个单一的“持久资源”。有没有可能发生这种情况?某些SQL2005/8配置导致它不支持单阶段通知?

    更新2:

    个人而言,重新调查了每个人的SQL Server版本-“dev 3”实际上都有sql2008,“dev 4”实际上是sql2005。这将教会我不再信任我的同事。;)由于数据的变化,我确信我们已经找到了问题所在。我们的sql2008开发人员没有遇到这个问题,因为sql2008包含了大量sql2005所没有的令人敬畏的内容。

    它还告诉我,因为我们将要支持SQL2005,所以我们不能像以前那样使用TransactionScope,如果我们要使用TransactionScope,我们需要传递一个单独的sqlConnection对象……在sqlConnection不容易传递的情况下,这似乎有问题……它只是有一股全局的味道。al-sqlconnection实例。皮尤!

    更新3

    只是想澄清一下这个问题:

    SqL2008:

    • 允许单个TransactionScope内有多个连接(如上述示例代码所示)。
    • 警告1:如果这些多个sqlconnections被嵌套,即同时打开两个或多个sqlconnections,TransactionScope将立即升级到DTC。
    • 警告2:如果向其他 '持久资源' (即:另一个SQL Server,)它将立即升级到DTC

    SqL2005:

    • 在单个TransactionScope、Period中不允许多个连接。如果打开第二个sqlconnection,它将升级。

    更新4

    为了让这个问题更进一步 一团糟 很有用,为了更清楚起见,下面介绍如何使用 单一的 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的点开始保持打开状态,直到不再需要它为止。除了全局连接对象的代码气味之外,先打开连接后关闭连接与尽可能晚地打开连接并尽快关闭连接的逻辑是不一致的。

    6 回复  |  直到 6 年前
        1
  •  70
  •   shA.t Rami Jamleh    6 年前

    SQL Server 2008可以使用多个 SQLConnection 一体的 TransactionScope 如果连接不同时打开,则不会升级,这将导致多个“物理”TCP连接,因此需要升级。

    我看到您的一些开发人员有SQL Server 2005,另一些开发人员有SQL Server 2008。您确定您正确识别了哪些正在升级,哪些没有升级?

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

        2
  •  55
  •   Peter Meinl    13 年前
        3
  •  31
  •   hwiechers    15 年前

    那个代码 在连接到2005时导致升级。

    查看有关msdn的文档- http://msdn.microsoft.com/en-us/library/ms172070.aspx

    SQL Server 2008中的可升级事务

    在.NET Framework 2.0版中 和SQL Server 2005,打开第二个 TransactionScope内的连接 会自动提升 事务到完全分布式 事务,即使两个连接 正在使用相同的连接 串。在这种情况下,一个分布式的 事务增加了不必要的开销 这会降低性能。

    从SQL Server 2008和 .NET框架的3.5版, 本地交易不再 升级为分布式事务 如果在中打开了另一个连接, 上一个之后的事务 交易记录已关闭。这需要 如果您的代码是 已经在使用连接池和 在事务中登记。

    我无法解释为什么dev 3:windows 7 x64、sql2005成功,dev 4:windows 7 x64失败。你确定那不是反过来的吗?

        4
  •  9
  •   Community rcollyer    7 年前

    我不知道为什么这个答案被删除了,但这似乎有一些相关的信息。

    8月4日10点17:42回答 Eduardo

    1. 设置 错误的,错误的 在连接字符串上,以避免在事务上自动登记。

    2. Manually enlist connection as participants 在事务范围内。[ original article 过时的 或者这样做: How to prevent automatic MSDTC promotion [archive.is]

        5
  •  2
  •   Richard Ev    11 年前

    我不太确定嵌套连接是否是问题所在。我正在调用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();
            }
        }
    
        6
  •  1
  •   amateur    15 年前

    如果在内部使用的访问权限超过1个,则TransactionScope始终升级为DTC事务。以上代码在禁用DTC的情况下工作的唯一方法是,如果您两次都从连接池获得相同的连接,那么这是非常有可能的。

    “问题是,在我们一半的开发人员机器上,我们可以在禁用msdtc的情况下运行。” 是否确定已禁用;)