[英]Asynchronously commit or rollback a transaction scope
众所周知,在 .Net 中引入async
await
模式时, TransactionScope
被遗忘了。 如果我们试图在事务范围内使用一些await
调用,它们就会被破坏。
现在,由于范围构造函数选项,此问题已修复。
但在我看来仍然缺少一个部分,至少我无法找到如何以简单的“类似事务范围”的方式做到这一点:如何等待范围的提交或回滚?
提交和回滚也是 IO 操作,它们应该是可等待的。 但由于它们发生在作用域处置上,我们必须等待处置。 如果没有事务范围实现IAsyncDisposable
,这是IAsyncDisposable
,目前情况并非如此。
我也查看了System.Transactions.Transaction
接口:那里也没有可等待的方法。
我知道提交和回滚几乎只是向数据库发送一个标志,所以它应该很快。 但是对于分布式事务,这可能会不那么快。 无论如何,这仍然是一些阻塞 IO。
关于分布式案例,请记住这可能会触发两阶段提交。 在某些情况下,在第一阶段(准备)会征集额外的持久资源。 这通常意味着针对那些最近登记的资源发出一些额外的查询。 在提交期间发生的所有事情。
那么有没有办法等待事务范围? 还是System.Transactions.Transaction
代替?
注意:我不认为这是“是否可以异步提交/回滚 SqlTransaction? ”的重复。 SqlTransaction
比系统事务更受限制。 它们只能寻址 SQL-Server 并且从不分发。 其他一些事务确实有异步方法,例如Npgsql 。 现在对于事务范围/系统事务具有异步方法,可能需要DbTransaction
具有异步方法。 (我不知道系统事务的内部结构,但它可能正在使用这个 ADO.NET 合同。我们将连接加入系统事务的方式确实让我认为它并没有使用它。)
更新: DbTransaction
在 .Net Core 3.0 中确实有它们,请参阅#35012 (特别感谢Roji )。
到目前为止还没有办法实现它。 但他们正在努力
也许是一个迟到的答案,但您想要的基本上归结为一种可以轻松创建的语法糖。
概括您的问题,我实现了“异步使用”语法,它允许“使用”的主体和“处置”部分都可以等待。 这是它的外观:
async Task DoSomething()
{
await UsingAsync.Do(
// this is the disposable usually passed to using(...)
new TransactionScope(TransactionScopeAsyncFlowOption.Enabled),
// this is the body of the using() {...}
async transaction => {
await Task.Delay(100); // do any async stuff here...
transaction.Complete(); // mark transaction for Commit
} // <-- the "dispose" part is also awaitable
);
}
实现就像这样简单:
public static class UsingAsync
{
public static async Task Do<TDisposable>(
TDisposable disposable,
Func<TDisposable, Task> body)
where TDisposable : IDisposable
{
try
{
await body(disposable);
}
finally
{
if (disposable != null)
{
await Task.Run(() => disposable.Dispose());
}
}
}
}
与常规using
子句相比,错误处理有所不同。 使用UsingAsync.Do
,主体或处置抛出的任何异常都将包装在AggregateException
。 当 body 和 dispose 都抛出异常时,这很有用,并且可以在AggregateException
检查这两个异常。 使用常规using
子句,只会捕获由 dispose 抛出的异常,除非主体显式包含在try..catch
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.