MSDTC 事务被提升为分布在事务范围外执行的查询上

MSDTC transaction being promoted to distributed on query executed outside of transaction scope

我有一个 SQL Server 2008 数据库,有两个表,一个 .NET 4.5 应用程序使用 TransactionScope 对数据库有两个查询,每个都使用自己的单独连接共享一个 单个 连接字符串。

这段代码运行良好:

internal static void Main(string[] args)
{
    string connStr = string.Format(
        "Data Source={0};Initial Catalog={1};" +
        "Integrated Security=True", "My DB Server", "My Database");

    using (var scope = new TransactionScope())
    {
        string query1 = "(actual query not relevant)";
        var ds = GetSqlServerDataSet(query1, connStr);
        string query2 = "(actual query not relevant)";
        var ds1 = GetSqlServerDataSet(query2, connStr);
        scope.Complete();
    }
}

private static System.Data.DataSet GetSqlServerDataSet(string usingQuery, 
    string usingConnectionString, 
    params System.Data.SqlClient.SqlParameter[] withParameters)
{
    using (var ds = new System.Data.DataSet())
    using (var conn = new System.Data.SqlClient.SqlConnection(usingConnectionString))
    using (var command = new System.Data.SqlClient.SqlCommand(usingQuery, conn))
    {
        command.Parameters.AddRange(withParameters);

        using (var adapter = new System.Data.SqlClient.SqlDataAdapter(command))
        {
            adapter.Fill(ds);
        }

        return ds;
    }
}

现在,如果我在范围内抛出异常以测试回滚,那就是事情变得奇怪的时候。我正在使用我编写的引用 class 库来保存有关数据库异常的信息。这是相同类型的设置——相同的 SQL 服务器、.NET 版本、相同的 ADO.NET 代码等。它只是写入不同的数据库。

工作原理如下:我已将此方法添加到我的应用程序中:

private static void HandleUnhandledException(Object sender, System.UnhandledExceptionEventArgs e)
{
    ExceptionHandling.Core.Main.ProcessException((Exception) e.ExceptionObject);
    Environment.Exit(0);
}

并且我已将此行添加到我的 Main 方法的顶部:

AppDomain.CurrentDomain.UnhandledException += HandleUnhandledException;

现在当我抛出异常时,例如throw new Exception("blah"); 位于 Main 的底部,就在 scope.Complete() 之前,它会自动跳转到 HandleUnhandledException,其余的如我上面所述发生。这导致 System.Transactions.TransactionManagerCommunicationException 消息:

Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool.

在我的 class 库中调用 Connection.Open() 时会发生这种情况。

那么这里发生了什么?我知道错误告诉我什么,我知道 how to fix it。我的问题是 为什么首先会发生这种情况?我认为 using 语句会在它到达我的 HandleUnhandledException 方法之前负责回滚事务,所以这里不应该涉及两个事务。还是我错了?

发生这种情况是因为当您的 HandleUnhandledException 方法 运行s - 您仍在 using 块内并且它的 finally 部分尚未 运行 .使用此代码很容易验证:

class Program {
    static void Main(string[] args) {
        AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
        try {
            throw new Exception("test");
        }
        finally {
            Console.WriteLine("finally");
        }
    }

    private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine("handle exception");
    }
}

输出:

handle exception
Unhandled exception: System.Exception: test ...
finally

您可能需要阅读 this 问题(和评论,例如 Eric Lipper 的评论)以讨论相关行为。但我认为原因与你的问题不太相关——我们可以说它就是这样工作的。

鉴于该信息,您很清楚为什么会观察到异常 - 您仍在未提交的事务范围内并尝试打开与不同数据库的连接。

在你的情况下想到的一个解决方案是检查我们是否在事务内部,如果是则抑制它(因为无论如何你都不希望你的日志代码以任何方式影响环境事务):

private static void HandleException(Exception ex) {
    TransactionScope scope = null;
    if (Transaction.Current != null)
        scope = new TransactionScope(TransactionScopeOption.Suppress);
    try {
        // do stuff
    }
    finally {
        scope?.Dispose();
    }
}

或者也许总是 运行 被压制 TransactionScope