易失性IEnlistmentNotification,TransactionScope.AsyncFlowEnabled = true和复杂的异步/等待

时间:2014-12-09 10:37:32

标签: c# async-await transactionscope

这是以下问题的后续问题:

Volatile IEnlistmentNotification and TransactionScope.AsyncFlowEnabled = true

只要您不等待多个陈述,上述问题中接受的方法就有效。让我举个例子:

public class SendResourceManager : IEnlistmentNotification
{
    private readonly Action onCommit;

    public SendResourceManager(Action onCommit)
    {
        this.onCommit = onCommit;
    }

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

    public void Commit(Enlistment enlistment)
    {
        Debug.WriteLine("Committing");
        this.onCommit();
        Debug.WriteLine("Committed");
        enlistment.Done();
    }

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

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

public class AsyncTransactionalMessageSender : ISendMessagesAsync
{
    private readonly List<Message> sentMessages = new List<Message>();

    public IReadOnlyCollection<Message> SentMessages
    {
        get { return new ReadOnlyCollection<Message>(this.sentMessages); }
    }

    public async Task SendAsync(Message message)
    {
        if (Transaction.Current != null)
        {
            await Transaction.Current.EnlistVolatileAsync(
                new SendResourceManager(async () => await this.SendInternal(message)), 
                EnlistmentOptions.None);
        }
        else
        {
            await this.SendInternal(message);
        }
    }

    private async Task SendInternal(Message message)
    {
        Debug.WriteLine("Sending");
        await Task.Delay(1000);
        this.sentMessages.Add(message);
        Debug.WriteLine("Sent");
    }
}

    [Test]
    public async Task ScopeRollbackAsync_DoesntSend()
    {
        var sender = new AsyncTransactionalMessageSender();
        using (var tx = new System.Transactions.TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            await sender.SendAsync(new Message("First"));
            await sender.SendAsync(new Message("Second"));
            await sender.SendAsync(new Message("Last"));

            // We do not commit the scope
        }

        sender.SentMessages.Should().BeEmpty();
    }

    [Test]
    public async Task ScopeCompleteAsync_Sends()
    {
        var sender = new AsyncTransactionalMessageSender();
        using (var tx = new System.Transactions.TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            await sender.SendAsync(new Message("First"));
            await sender.SendAsync(new Message("Second"));
            await sender.SendAsync(new Message("Last"));

            tx.Complete();
        }

        sender.SentMessages.Should().HaveCount(3)
            .And.Contain(m => m.Value == "First")
            .And.Contain(m => m.Value == "Second")
            .And.Contain(m => m.Value == "Last");
    }

一旦你引入了如上例所示的Task.Delay,生成的异步状态机就永远不会回来并调用this.sentMessages.Add(message)Debug.WriteLine("Sent")

问题是我现在正在看到如何在登记通知中正确登记异步代码。任何想法如何应对这一挑战?

0 个答案:

没有答案