简体   繁体   中英

Asynchronously commit or rollback a transaction scope

As many knows, TransactionScope were forgotten when the async await pattern was introduced in .Net. They were broken if we were trying to use some await call inside a transaction scope.

Now this is fixed thanks to a scope constructor option .

But it looks to me there is still a missing piece, at least I am unable to find how to do that in a simple "transaction scope like" way: how to await the commit or rollback of a scope?

Commit and rollback are IO operations too, they should be awaitable. But since they happen on scope disposal, we have to await the dispose. That is not doable without having IAsyncDisposable implemented by transaction scopes, which is not currently the case.

I had a look at the System.Transactions.Transaction interface too: no awaitable methods there either.

I understand that committing and rollbacking is almost just sending a flag to the database, so it should be fast. But with distributed transactions, that could be less fast. And anyway, that is still some blocking IO.

About distributed cases, remember this may trigger a two phases commit. In some cases additional durable resources are enlisted during the first phase (prepare). It then usually means some additional queries are issued against those lately enlisted resources. All that happening during the commit.

So is there any way to await a transaction scope? Or a System.Transactions.Transaction instead?

Note: I do not consider this to be a duplicate of " Is it possible to commit/rollback SqlTransaction in asynchronous? ". SqlTransaction are more limited than system transactions. They can address only SQL-Server and are never distributed. Some other transactions do have async methods, such as Npgsql . Now for having async methods on transaction scopes/system transaction, DbTransaction may be required to have async methods. (I do not know the internals of system transaction, but it is maybe using this ADO.NET contract. The way we enlist connection into system transaction does let me think it does not use it though.)
Update: DbTransaction does have them in .Net Core 3.0, see #35012 (notably thanks to Roji ).

There's no way to implement it so far. But they work on it

Maybe a late answer, but what you want basically boils down to a kind of syntactic sugar that can be easily created on your own.

Generalizing your problem, I implemented an "async using" syntax, which allows both the body and the "dispose" part of the "using" to be awaitable. Here is how it looks:

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
    );
}

The implementation is as simple as this:

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());
            }
        }
    }
}

There is a difference in error handling, compared to the regular using clause. When using UsingAsync.Do , any exception thrown by the body or the dispose will be wrapped inside an AggregateException . This is useful when both body and dispose each throw an exception, and both exceptions can be examined in an AggregateException . With the regular using clause, only the exception thrown by dispose will be caught, unless the body is explicitly wrapped in try..catch .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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