如何在.NET 4.0中使用Microsoft.Bcl.Async支持TransactionScope中的异步方法?

时间:2014-07-06 05:46:13

标签: c# .net .net-4.0 async-await transactionscope

我的方法类似于:

public async Task SaveItemsAsync(IEnumerable<MyItem> items)
{
    using (var ts = new TransactionScope())
    {
        foreach (var item in items)
        {
            await _repository.SaveItemAsync(item);
        }

        await _repository.DoSomethingElse();

        ts.Complete();
    }
}

这当然有问题,因为TransactionScope与async / await没什么关系。

它失败并显示InvalidOperationException消息:

  

&#34; TransactionScope必须放在与其创建的同一个线程上。&#34;

我在TransactionScopeAsyncFlowOption中读到了this answer,这似乎正是我所需要的。

但是,对于这个特定的项目,我很难支持.Net 4.0,无法升级到4.5或4.5.1。因此,我的项目中的异步/等待行为由Microsoft.Bcl.Async NuGet Package提供。

我似乎无法在此OOB package中找到TransactionScopeAsyncFlowOption。我在某处错过了吗?

如果没有,是否有其他方法可以达到相同的效果?也就是说 - 我希望事务范围能够正确地完成或回滚,尽管跨越线程有延续。

我在上面的示例中添加了DoSomethingElse来说明在事务范围内可能有多个调用,因此在一次调用中简单地将所有项目传递给数据库是不可行的选择。

如果重要,存储库使用直接ADO.Net(SqlConnectionSqlCommand等)来写入SQL Server。

更新

我以为我有一个解决方案,涉及从.Net 4.5.1中获取System.Transactions.dll并将其包含在我的项目中。但是,我发现这只适用于我的开发盒,因为它已经安装了4.5.1。部署到仅具有.Net 4.0的计算机时,它不起作用。它只给了MissingMethodException。我正在寻找适用于.Net 4.0安装的解决方案。

3 个答案:

答案 0 :(得分:19)

您必须使用TransactionScopeAsyncFlowOption.Enabled

public static TransactionScope CreateAsyncTransactionScope(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted)
    {
        var transactionOptions = new TransactionOptions
        {
            IsolationLevel = isolationLevel,
            Timeout = TransactionManager.MaximumTimeout
        };
        return new TransactionScope(TransactionScopeOption.Required, transactionOptions, TransactionScopeAsyncFlowOption.Enabled);
    }

答案 1 :(得分:2)

TransactionScope在框架4.5.1中已得到解决,以处理异步/等待操作。不要与4.5一起使用!!!

使用EF6和DbContextTransaction作为替代。

using (Entities entities = new Entities())
    using (DbContextTransaction scope = entities.Database.BeginTransaction())
    {
        entities.Database.ExecuteSqlCommand("SELECT TOP 1 KeyColumn FROM MyTable)");
        scope.Commit();
    }

更多信息

TransactionScope和Async / Await。与潮流合而为一! Daniel Marbach于2015年8月6日撰写 您可能不知道这一点,但是.NET Framework的4.5.0版本包含一个有关System.Transactions.TransactionScope及其在async / await中的行为的严重错误。由于存在此错误,因此TransactionScope无法进入异步继续。这可能会更改事务的线程上下文,从而导致在处置事务范围时引发异常。

这是一个大问题,因为它使编写涉及事务的异步代码极易出错。

好消息是,作为.NET Framework 4.5.1的一部分,Microsoft发布了该“异步延续”错误的修复程序。事实是,像我们这样的开发人员现在需要明确加入才能获得这种新行为。让我们看看如何做到这一点。

TL; DR

如果同时使用TransactionScope和async / await,则应立即真正升级到.NET 4.5.1。 TransactionScope包装的异步代码需要在其构造函数中指定TransactionScopeAsyncFlowOption.Enabled。

答案 2 :(得分:-3)

不确定这是否适合您的方案,但可以在ASP.NET应用程序中使用ConfigureAwait(false)以确保等待的函数调用重新进入调用请求上下文。

因此,如果此代码在ASP.NET应用程序中运行,则使用以下代码:

await _repository.SaveItemAsync(item).ConfigureAwait(false);

确保在请求线程上继续执行。