异步提交或回滚事务范围

时间:2017-05-24 13:35:56

标签: c# .net async-await transactionscope

众所周知,{。1}} TransactionScope模式在.Net中引入时,async被遗忘了。如果我们试图在事务范围内使用一些await调用,它们就会被破坏。

现在由于scope constructor option而修复了这个问题。

但在我看来还有一个缺失的部分,至少我无法在一个简单的交易范围内找到如何做到这一点,比如"方式:如何等待提交或回滚范围?

提交和回滚也是IO操作,它们应该是等待的。但由于它们发生在范围处理上,我们将不得不等待处置。这看起来并不可行(await模式也不实用)。

我也看了System.Transactions.Transaction界面:没有等待的方法。

我知道提交和回滚几乎只是向数据库发送一个标志,所以它应该很快。但是对于分布式事务,这可能会更快。无论如何,这仍然是一些阻止IO。

关于分布式案例,请记住这可能会触发两阶段提交。在某些情况下,在第一阶段(准备)期间会招募额外的耐用资源。然后通常意味着针对那些最近登记的资源发布了一些额外的查询。在提交期间发生的所有事情。

有没有办法等待交易范围?或者改为using

注意:我不认为这是" Is it possible to commit/rollback SqlTransaction in asynchronous?"的副本。 System.Transactions.Transaction比系统事务更受限制。它们只能处理SQL-Server,并且永远不会分发。其他一些事务确实有异步方法,例如Npgsql。现在,对于在事务范围/系统事务上使用异步方法,可能需要DbTransaction来使用异步方法。 (我不知道系统事务的内部,但它可能使用这个ADO.NET契约。我们将连接添加到系统事务中的方式让我觉得它不会使用它。)

2 个答案:

答案 0 :(得分:6)

到目前为止,尚无办法实现。但是they work on it

答案 1 :(得分:1)

也许是一个迟到的答案,但你想要的基本上归结为一种可以很容易地自己创造的语法糖。

概括您的问题,我使用"实现了" async;语法,允许身体和" dispose" "使用"的一部分等待。以下是它的外观:

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时,正文或dispose抛出的任何异常都将包含在AggregateException中。当body和dispose都抛出异常时,这很有用,并且可以在AggregateException中检查这两个异常。使用常规using子句,只会捕获dispose抛出的异常,除非正文明确地包含在try..catch中。