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

Posted

技术标签:

【中文标题】如何在 .NET 4.0 中使用 Microsoft.Bcl.Async 支持 TransactionScope 中的异步方法? [复制]【英文标题】:How to support async methods in a TransactionScope with Microsoft.Bcl.Async in .NET 4.0? [duplicate] 【发布时间】:2014-07-06 05:46:13 【问题描述】:

我有一个类似的方法:

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 的消息:

“TransactionScope 必须在创建它的同一线程上处理。”

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

但是,对于这个特定的项目,我有一个支持 .Net 4.0 的硬性要求,并且无法升级到 4.5 或 4.5.1。因此,我的项目中的 async/await 行为由 Microsoft.Bcl.Async NuGet Package 提供。

我似乎在这个OOB package 或任何其他OOB package 中找不到TransactionScopeAsyncFlowOption。我只是在某个地方想念它吗?

如果不可用,是否有替代方法可以达到相同的结果?也就是说 - 我希望事务范围能够正确完成或回滚,尽管线程之间存在延续。

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

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

更新 1

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

更新 2

我最初在 2014 年 7 月提出这个问题。.NET Framework 4.0、4.5 和 4.5.1 于 2016 年 1 月到达end of life。因此该问题不再适用,此处仅供历史参考。

【问题讨论】:

你可以随时拿the source code,清理一下,自己实现 如果可扩展性不是主要因素,您可以引入线程关联like that。 @YuvalItzchakov - 是的,我可以看到它是如何在 System.Transactions 中实现的,但我自己实现它需要重写内部私有方法,如 PushScopePopScope - 这最终需要重写整个组装... @Noseratio - 不,它不是 ASP.Net。这是一个自定义的 Windows 服务应用程序。如果您能提供一个答案来说明您为这个最小示例提出的技术,我很感兴趣。我查看了您的其他链接,但我没有建立联系... @Noseratio - 真可惜。不错的尝试,感谢您的测试。我正在考虑几种不同的技术。 1) 操纵 TLS。 2)明确传递交易。 3) 说服客户升级到 4.5.1。我认为#3是最好的路线。 :) 【参考方案1】:

在 .NET Framework 4.0 中无法实现这一点。此外,.NET Framework 4.0 reached end of life on 2016-01-12,因此不再相关。

要在 .NET 中支持异步方法中的事务范围(自 .NET Framework 4.5.1 起),请使用 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);
    

【讨论】:

虽然我刚刚看到你因为某种原因找不到那个选项,这很奇怪。 重要提示:TransactionScopeAsyncFlowOption 仅适用于 .net framework 4.5.1 或更高版本particular.net/blog/… 我将此标记为已接受的答案,因为 .NET Framework 4.0 不再相关。谢谢。 @MattJohnson-Pint 此答案未按要求回答问题,也未按标题回答。我认为你提出的问题是一个非常好的问题!虽然“升级到 4.5.1 并否定问题”当然是正确的解决方法,但未来的其他人可能无法做到这一点。我认为以下 PitJ 的答案是其他人将来可能需要的答案,也是使这个问题对社区有用的答案。 或者...请编辑问题和标题以删除 .NET 4.0 限制。 ...此时它将作为***.com/questions/13543254/…的欺骗而关闭【参考方案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 和异步/等待。与流合一! 作者 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。

【讨论】:

【参考方案3】:

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

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

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

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

【讨论】:

我相信你已经搞反了。 ConfigureAwait(true)(默认值,因此您不必指定它)意味着在延续中同步回原始上下文。当您不需要需要该上下文并希望避免安排它的额外开销时,应指定ConfigureAwait(false) @ToddMenier 它没有解释上面的错误,那么。

以上是关于如何在 .NET 4.0 中使用 Microsoft.Bcl.Async 支持 TransactionScope 中的异步方法? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何在 .net framework 4.0 中压缩文件夹

如何使用 .Net 4.0 中包含的 HttpClient 类将文件上传到在 IIS Express 中运行的 Asp.Net MVC 4.0 操作

如何在 asp.net 4.0 中获取所有网络打印机

如何使用 .Net 4.0 REST WCF 服务?

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

如何在 .NET 4.0 中请求超时或取消之前“休眠”