TransactionScope - 事务性 WCF 和 paralellism/multithreading

TransactionScope - Transactional WCF and paralellism/multithreading

我正在寻找我们项目中的一个错误,我们 有时 看到数据被保存到数据库中,即使发生异常并且一切都应该回滚。我有 ,发现如果我在创建新的 TransactionScope 后立即查询数据库 @@trancount,结果得到 0,我没有 "valid"交易。它以某种方式 aborted/rolled 被另一个线程返回。

我用来复现的代码真的很简单:

我在Paralell.For中调用这个方法几千次

void Handle()
{
    try
    {
        using (var transaction = new TransactionScope(TransactionScopeOption.Required)
        {
            // Getting @@trancount right here enables us verify whether we have a valid transaction.

            WriteImportantBusinessDataToDatabase("This will sometimes be committed even if the WCF call below fails!");

            // Making a transactional WCF call
            var serviceClient = new Service1Client("WSHttpBinding_IService1");
            serviceClient.DoWork(); // We get an exception here

            WriteImportantBusinessDataToDatabase("We never reach this location");

            transaction.Complete();
        }
    }
    catch (Exception e)
    {
        Logger.Error(e, "Something failed. {message}", e.Message);
    }
}

WCF 服务是事务性的,TransactionFlowOption.Mandatory

[ServiceContract]
public interface IService1
{
    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Mandatory)]
    void DoWork();
}

[ServiceBehavior]
public class Service1 : IService1
{
    [OperationBehavior(TransactionScopeRequired = true)]
    public void DoWork()
    {
        throw new Exception();
    }
}

@@trancount 预计在创建新的 TransactionScope 后立即 1。但是有时 @@trancount0,在更罕见的情况下 2System.Transactions.Transaction.Current.TransactionInformation.Status 属性 始终是 Active,无论 @@trancount 是什么。插入数据库执行得很好。问题发生然后本地事务被 WCF 调用提升为分布式事务。然后我们得到一个异常说 "The transaction has aborted."

方法名称 Handle 正确地表明这确实是一个 NServiceBus 处理程序,日夜处理数十万条消息。仅当 WCF 调用的另一端存在问题时,我们才会遇到此不一致问题。

可以找到此设置的可运行版本 on my GitHub

我已经重现了从 4.5.1 到 4.8 的所有 .Net 框架版本的问题。所有这些都产生相同的结果。谁能解释一下这里发生了什么?

这是由.Net 的连接共享机制中某处的错误引起的。该错误在 4.5.1 和 4.8 之间的所有 .Net Framework 版本以及新的 System.Data.SqlClient package.

上重现

一个issue has been added to the System.Data.SqlClient repository.