Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Context is lost when reassigning Transaction.Current in async/await code

When reassigning Transaction.Current, I seem to lose the original TransactionScopeAsyncFlowOption behavior of my TransactionScope. The code after the second await loses it's Transaction.Current value.

Sample code (Linqpad):

async Task Main()
{
    Thread.CurrentThread.ManagedThreadId.Dump("before await");
    var scope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled);
    var transaction = Transaction.Current;

    await Task.Delay(1000).ConfigureAwait(false);

    Thread.CurrentThread.ManagedThreadId.Dump("after first await");
    Transaction.Current = transaction;
    Transaction.Current.Dump(); // not null

    await Task.Delay(1000).ConfigureAwait(false);

    Thread.CurrentThread.ManagedThreadId.Dump("after second await");
    Transaction.Current.Dump(); // is null :/
}

I know I should be using a using statement on TransactionScope rather than reassigning the ambient transaction, however because of reasons, it's not possible to do that. I'm curious about the reason of the behavior of the snippet above and wonder if there are ways to keep the original TransactionScopeAsyncFlowOption behavior.

like image 268
ThomasDC Avatar asked Nov 07 '22 17:11

ThomasDC


1 Answers

I managed to fix the issue by wrapping the Transaction object in another TransactionScope with TransactionScopeAsyncFlowOption enabled:

async Task Main()
{
    var scope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled);
    var transaction = Transaction.Current;

    await Task.Delay(1000);

    Transaction.Current = transaction;
    Debug.Assert(Transaction.Current != null); // not null

    await Task.Delay(1000);

    Debug.Assert(Transaction.Current == null); // is null :/

    using (var innerScope = new TransactionScope(transaction, TransactionScopeAsyncFlowOption.Enabled))
    {
        // Transaction.Current state is kept across async continuations
        Debug.Assert(Transaction.Current != null); // not null
        await Task.Delay(10);
        Debug.Assert(Transaction.Current != null); // not null
        innerScope.Complete();
    }
}
like image 126
ThomasDC Avatar answered Nov 13 '22 14:11

ThomasDC