译文:TransactionScope 与 Async/Await
你可能不知道这一点,在 .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的更多相关文章
- 译文: async/await SynchronizationContext 上下文问题
async / await 使异步代码更容易写,因为它隐藏了很多细节. 许多这些细节都捕获在 SynchronizationContext 中,这些可能会改变异步代码的行为完全由于你执行你的代码的环境 ...
- (译文)学习ES6非常棒的特性——Async / Await函数
try/catch 在使用Async/Await前,我们可能这样写: const main = (paramsA, paramsB, paramsC, done) => { funcA(para ...
- async/await 内幕【译文】
C# Under the Hood: async/await 原文地址:https://www.markopapic.com/csharp-under-the-hood-async-await/ 前言 ...
- [译]async/await中阻塞死锁
这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:Don'tBlock o ...
- [译]async/await中使用阻塞式代码导致死锁 百万数据排序:优化的选择排序(堆排序)
[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Cleary的 ...
- [译]async/await中使用阻塞式代码导致死锁
原文:[译]async/await中使用阻塞式代码导致死锁 这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁.内容主要是从作者Stephen Clea ...
- JavaScript 如何工作的: 事件循环和异步编程的崛起 + 5 个关于如何使用 async/await 编写更好的技巧
原文地址:How JavaScript works: Event loop and the rise of Async programming + 5 ways to better coding wi ...
- 图与例解读Async/Await
JavaScript ES7的async/await语法让异步promise操作起来更方便.如果你需要从多个数据库或者接口按顺序异步获取数据,你可能最终写出一坨纠缠不清的promise与回调.然而使用 ...
- 【限时免费】AppBoxCore - 细粒度权限管理框架(EFCore+RazorPages+async/await)!
目录 前言 全新AppBoxCore RazorPages 和 TagHelpers 技术架构 页面处理器和数据库操作的异步调用 Authorize特性和自定义权限验证过滤器 Authorize登录授 ...
随机推荐
- ubuntu中使用nginx把本地80端口转到其他端口
ubuntu中使用nginx把本地80端口转到其他端口 因为只是在开发的过程中遇到要使用域名的方式访问, 而linux默认把1024以下的端口全部禁用. 在网上找了N多方式开启80端口无果后, 方才想 ...
- .Net程序员学习Linux(三)
基础命令 ll 文件名 命令可以查看文件的大小 file 文件名 可以看到文件后缀,大小 压缩与解压工具 这些压缩工具按照我理解应该是很少单独拿出来用,应该是需要配合其他命令或者工具来使用 gzi ...
- 在VS中关于MySQL的相关问题
最近在vs上折腾mysql数据库 遇到了一些小问题,这里记录一下 问题一:数据源选择中没有mysql数据库的选项 解放方法: 1.安装MySql的VS插件(版本请下载最新版)mysql-for-vis ...
- Http请求通信(工具类)
Http请求通信(工具类) 异步消息处理流程是: 首先需要在主线程当中创建一个Handle对象,并重写handlerMessage()方法. 然后当子线程中需要进行UI操作时,就创建一个Message ...
- struts -执行流程
When a client request is given, a web container will receive request Web container loads web.xml and ...
- 一步一步建MVC
http://www.cnblogs.com/yuangang/p/5569518.html
- 修改PYTHONPATH的一种方法(在Window平台和Ubuntu下都有效)
1.显示PYTHONPATH 2.修改PYTHONPATH 通过PYTHONPATH 中的任何 .pth 文件来添加pythonpath.比如我想添加/home/aa这个路径到pythonpath里, ...
- WCF大文件传输服务
由于项目需要,自己写一个基于WCF的大文件传输服务雏形.觉得有一定的参考价值,因此放在网上分享. 目前版本为v1.1特点如下: 1.文件传输端口为18650 2.上传和下载文件 3.支持获取文件传输状 ...
- 配置hive元数据库mysql时候出现 Unable to find the JDBC database jar on host : master
解决办法: cd /usr/share/java/,(没有java文件夹,自行创建)rz mysql-connector-java-***.jar,mv mysql-connector-java-* ...
- Zend Studio 11.0.2 破解和汉化
本方法适用于Zend Studio 11.0.2,亲测,其他版本未知. 破解方法:覆盖安装目录 plugins 里同名文件,启动任意输入即可注册. Windows版下载地址:http://downlo ...