Volatile IEnlistmentNotification and TransactionScope.AsyncFlowEnabled = true

In addition to .NET 4.5.1, TransactionScope has a new option that allows you to use an asynchronous stream. This allows you to write the following client code

using(var txt = new TransactionScope(..., TransactionScopeAsyncFlowOption.Enabled)
{
   await sender.SendAsync();
}

      

So far so good. But when I need to implement a mutable IEnlistmentNotification, I struggle to do it. Imagine the following scenario, assumption: my underlying infrastructure is completely bottom-up asynchronous

public class MessageSender : ISendMessages
{
    public async Task SendAsync(TransportMessage message, SendOptions options)
    {
        await sender.SendAsync(message);
    }
}

      

So what I want to achieve is introduce a mutable IEnlistmentNotification like this:

internal class SendResourceManager : IEnlistmentNotification
{
    private readonly Func<Task> onCommit;

    public SendResourceManager(Func<Task> onCommit)
    {
        this.onCommit = onCommit;
    }

    public void Prepare(PreparingEnlistment preparingEnlistment)
    {
        preparingEnlistment.Prepared();
    }

    public void Commit(Enlistment enlistment)
    {
        await this.onCommit();
        enlistment.Done();
    }

    public void Rollback(Enlistment enlistment)
    {
        enlistment.Done();
    }

    public void InDoubt(Enlistment enlistment)
    {
        enlistment.Done();
    }
}

      

and new sender

public class MessageSender : ISendMessages
{
    public async Task SendAsync(TransportMessage message, SendOptions options)
    {
        // Dirty: Let assume Transaction.Current is never null
        Transaction.Current.EnlistVolatile(new SendResourceManager(async () => { await sender.SendAsync(message) }));
    }
}

      

Note. Of course, this code doesn't compile. I would need to declare an async void commit method. It's horrible.

So my question is, how can I write a call that can wait awaiting an asynchronous operation?

+3


source to share


1 answer


As long as EnlistVolatile

it is not a cpu heavy operation, you can create a thin wrapper Task

on top EnlistVolatile

using Task.FromResult

:

public static class TranscationExtensions
{
    public static Task EnlistVolatileAsync(this Transaction transaction, 
                                           IEnlistmentNotification 
                                           enlistmentNotification, 
                                           EnlistmentOptions enlistmentOptions)
    {
        return Task.FromResult(transaction.EnlistVolatile
                                           (enlistmentNotification, 
                                            enlistmentOptions));
    }
}

      

and then use it inside your method:



public class MessageSender : ISendMessages
{
    public Task SendAsync(TransportMessage message, SendOptions options)
    {
        return Transaction.Current.EnlistVolatileAsync
               (new SendResourceManager(async () => 
                                       { await sender.SendAsync(message) }));
    }
}

      

which you would expect in your stack above:

MessageSender sender = new MessageSender();
await sender.SendAsync(message, options);

      

+3


source







All Articles