繁体   English   中英

异步提交或回滚事务范围

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM