Подтвердить что ты не робот

Асинхронно совершать или откатывать область транзакций

Как известно, TransactionScope были забыты, когда шаблон async await был введен в .Net. Они были сломаны, если мы пытались использовать какой-либо вызов await внутри области транзакции.

Теперь это исправлено благодаря конструктору возможностей > .

Но мне кажется, что все еще есть недостающая часть, по крайней мере, я не могу найти, как это сделать в простой форме транзакции вроде: как ждать фиксации или отката области?

Commit и rollback также являются операциями ввода-вывода, они должны быть ожидаемыми. Но так как они происходят по распоряжению областью, нам придется ждать распоряжения. Это не выглядит выполнимым (или практичным с шаблоном using).

Я также посмотрел на System.Transactions.Transaction: нет ожидаемых методов.

Я понимаю, что фиксация и откат - это просто отправка флага в базу данных, поэтому она должна быть быстрой. Но с распределенными транзакциями это может быть менее быстрым. И вообще, это все еще некоторая блокировка IO.

О распределенных случаях, помните, что это может привести к фиксации двух фаз. В некоторых случаях на первом этапе (подготовка) зачисляются дополнительные долговременные ресурсы. Это обычно означает, что некоторые дополнительные запросы выдаются против тех, кто недавно зачислен в ресурсы. Все, что происходит во время фиксации.

Итак, есть ли способ ждать области транзакции? Или вместо System.Transactions.Transaction?

Примечание: Я не считаю, что это дубликат "Возможно ли совершить/отменить SqlTransaction в асинхронном?". SqlTransaction более ограничены, чем системные транзакции. Они могут обращаться только к SQL-Server и никогда не распространяются. Некоторые другие транзакции имеют асинхронные методы, такие как Npgsql. Теперь для методов async для транзакций/системных транзакций DbTransaction может потребоваться использовать методы async. (Я не знаю внутренних системных транзакций, но возможно, используя этот контракт ADO.NET. Способ подключения к системной транзакции позволяет мне думать, что он не использует его, хотя.)

4b9b3361

Ответ 1

Возможно, поздний ответ, но то, что вы хотите, в основном сводится к синтаксическому сахару, который можно легко создать самостоятельно.

Обобщая вашу проблему, я применил синтаксис "async using", который позволяет обеим сторонам и "распоряжаться" частью "использования" быть ожидаемым. Вот как это выглядит:

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. Это полезно, когда и тело, и распоряжение бросают исключение, и оба исключения могут быть рассмотрены в AggregateException. С помощью обычного предложения using будет исключено только исключение, созданное командой dispose, если тело явно не упаковано в try..catch.