你可能不知道这一点,在 .NET Framework 4.5.0  版本中包含有一个关于 System.Transactions.TransactionScope 在与 async/await 一起工作时会产生的一个严重的 bug 。由于这个错误,TransactionScope 不能在异步代码中正常操作,它可能更改事务的线程上下文,导致在处理事务作用域时抛出异常。

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

好消息是,在 .NET Framework 4.5.1 版本中,微软发布了这个 "异步连接" 错误的修复程序。作为开发者的我们需要明确的做到以下两点:

  • 如果说你在 TransactionScope 代码中使用 async/await,你需要将框架升级到 .NET 4.5.1 或以上版本。
  • 在有包装异步代码的 TransactionScope 的构造函数中指定 TransactionScopeAsyncFlowOption.Enabled .

TransactionScope

System.Transactions.TransactionScope 类允许我们在事务中包装数据库代码,基础结构代码,有时甚至包括第三方代码(如果第三方库支持)。 然后,代码只在我们实际想提交(或完成)事务时才执行操作。

只要 TransactionScope 中的所有代码都在同一线程上执行,调用堆栈上的所有代码都可以与代码中定义的 TransactionScope 一起参与。 我们可以在父事务作用域内嵌套作用域或创建新的独立作用域。 我们甚至可以创建 TransactionScope 的副本,并将副本传递到另一个线程并连接回调用线程。 通过使用事务作用域包装代码,使用隐式事务模型,也称为环境事务。

如下代码:

public void TransactionScopeAffectsCurrentTransaction() {
Debug.Assert(Transaction.Current == null); using (var tx = new TransactionScope()) {
Debug.Assert(Transaction.Current != null); SomeMethodInTheCallStack(); tx.Complete();
} Debug.Assert(Transaction.Current == null);
} private static void SomeMethodInTheCallStack()
{
Debug.Assert(Transaction.Current != null);
}

正如我们可以看到使用块外面的 Transaction.Current 属性为 null。 在使用块内 Transaction.Current 属性不为 null。 即使在调用堆栈中的方法像 SomeMethodInTheCallStack 可以访问 Transaction.Current,前提是它要被包裹在使用块中。
TransactionScope 的好处是,如果需要,本地事务自动升级到分布式事务。 事务范围还简化了对事务的编程,如果你喜欢隐式的显式。

TransactionFlowInterruptedException

当 async / await 引入了 C#5.0 和 .NET 4.5,一个小小的细节被完全忘记。 当在一个包装 TransactionScope 下调用一个异步方法时,编译器引入的底层状态机没有正确地“浮动”事务(原文 "float")。 让我们将我们关于 TransactionScope 如何在同步代码中工作的知识应用到异步代码。

有如下代码:

public async Task TransactionScopeWhichDoesntBehaveLikeYouThinkItShould() {
using (var tx = new TransactionScope())
{
await SomeMethodInTheCallStackAsync()
.ConfigureAwait(false); tx.Complete();
}
} private static async Task SomeMethodInTheCallStackAsync()
{
await Task.Delay().ConfigureAwait(false);
}

不幸的是,它不工作的方式。 代码几乎(但只是几乎)执行类似于同步版本,但如果项目这个代码是写在目标 .NET Framework 4.5,当我们到达使用块的结束,并尝试处置 TransactionScope 时抛出以下异常 :
  System.InvalidOperationException:一个 TransactionScope 必须处理在它被创建的同一个线程。

为了使TransactionScope和async正常工作,我们需要将我们的项目升级到.NET 4.5.1。

TransactionScopeAsyncFlowOption

在 .NET 4.5.1中,TransactionScope 有一个名为 TransactionScopeAsyncFlowOption 的新枚举,可以在构造函数中提供。 您必须通过指定,明确地选择跨线程连续的事务流,如下:

using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
await SomeMethodInTheCallStackAsync()
.ConfigureAwait(false); tx.Complete();
}

你可能很好奇,默认的 TransactionScopeAsyncFlowOption 是 Suppress(阻止的),因为微软想避免破坏 .NET 4.5.0 版本中代码库中行为。

最后

使用 TransactionScope 结合 async / await 时,你应该更新所有使用 TransactionScope 的代码路径以启用 TransactionScopeAsyncFlowOption.Enabled 。 这样才能使事务能够正确地流入异步代码,防止在TransactionScope下使用时业务逻辑不正常。

原文:TransactionScope and Async/Await. Be one with the flow!

译文:TransactionScope 与 Async/Await的更多相关文章

  1. 译文: async/await SynchronizationContext 上下文问题

    async / await 使异步代码更容易写,因为它隐藏了很多细节. 许多这些细节都捕获在 SynchronizationContext 中,这些可能会改变异步代码的行为完全由于你执行你的代码的环境 ...

  2. (译文)学习ES6非常棒的特性——Async / Await函数

    try/catch 在使用Async/Await前,我们可能这样写: const main = (paramsA, paramsB, paramsC, done) => { funcA(para ...

  3. async/await 内幕【译文】

    C# Under the Hood: async/await 原文地址:https://www.markopapic.com/csharp-under-the-hood-async-await/ 前言 ...

  4. [译]async/await中阻塞死锁

    这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:Don'tBlock o ...

  5. [译]async/await中使用阻塞式代码导致死锁 百万数据排序:优化的选择排序(堆排序)

    [译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的 ...

  6. [译]async/await中使用阻塞式代码导致死锁

    原文:[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Clea ...

  7. JavaScript 如何工作的: 事件循环和异步编程的崛起 + 5 个关于如何使用 async/await 编写更好的技巧

    原文地址:How JavaScript works: Event loop and the rise of Async programming + 5 ways to better coding wi ...

  8. 图与例解读Async/Await

    JavaScript ES7的async/await语法让异步promise操作起来更方便.如果你需要从多个数据库或者接口按顺序异步获取数据,你可能最终写出一坨纠缠不清的promise与回调.然而使用 ...

  9. 【限时免费】AppBoxCore - 细粒度权限管理框架(EFCore+RazorPages+async/await)!

    目录 前言 全新AppBoxCore RazorPages 和 TagHelpers 技术架构 页面处理器和数据库操作的异步调用 Authorize特性和自定义权限验证过滤器 Authorize登录授 ...

随机推荐

  1. springmvc获得项目根目录(绝对路径)

    原文:http://www.bubuko.com/infodetail-790547.html 在项目中有时候我们需要记录日志或者上传图片,需要知道项目的具体路径,如果项目中使用了spring,那么获 ...

  2. http://bbs.phpcms.cn/thread-266337-1-1.html

    http://bbs.phpcms.cn/thread-266337-1-1.html utf8  改成 ANSI”

  3. CSS background-position随笔

    1.定义和用法 background-position 属性设置背景图像的起始位置. 这个属性设置背景原图像(由 background-image 定义)的位置,背景图像如果要重复,将从这一点开始. ...

  4. 如何写robots.txt?

    robin 发表在 八月 2, 2006 在国内,网站管理者似乎对robots.txt并没有引起多大重视,应一些朋友之请求,今天想通过这篇文章来简单谈一下robots.txt的写作. robots.t ...

  5. 一个js加css加html完成的HTML

    效果图: 代码: HTML: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "htt ...

  6. 关于java中for和foreach循环

    for循环中的循环条件中的变量只求一次值!具体看最后的图片 foreach语句是java5新增,在遍历数组.集合的时候,foreach拥有不错的性能. foreach是for语句的简化,但是forea ...

  7. Qt中使用信号和槽的一点心得

    信号(Signal)与槽(Slot)-Qt中的典型机制 这一篇文章中都说得很详细了,这里不再重复,只说一点在实际使用中可能会遇到的问题. 1.一个信号不要同时连接几个槽函数,不然执行的顺序是随机的,最 ...

  8. 343. Integer Break -- Avota

    问题描述: Given a positive integer n, break it into the sum of at least two positive integers and maximi ...

  9. [LeetCode OJ] Sort Colors

    Given an array with n objects colored red, white or blue, sort them so that objects of the same colo ...

  10. 关于Java IM的一点资料

    微信是腾讯采用自己开发的协议做的,其他市面上很多产品大都是采用XMPP协议,包括米聊在内之前也是这样子. 采用XMPP协议,服务端可以采用OpenFire搭建,客户端有个开源的叫asmack的东西,g ...