是否可以在自定义WCF服务行为中创建TransactionScope?(async,await,TransactionScopeAsyncFlowOption.Enabled)

Ajd*_*eek 8 c# wcf asynchronous transactionscope async-await

TL; DR?

截屏解释问题:https://youtu.be/B-Q3T5KpiYk

问题

将事务从客户端流向服务时Transaction.Current等待服务调用之后变为null.

除非您在服务方法中创建一个新的TransactionScope,如下所示:

[OperationBehavior(TransactionScopeRequired = true)]
public async Task CallAsync()
{
    using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
        await _service.WriteAsync();
        await _service.WriteAsync();            
        scope.Complete();
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么默认情况下不启用TransactionScopeAsyncFlowOption我不知道,但我不想重复自己,所以我想我总是使用自定义行为创建一个带有该选项的内部事务管理器.

问题更新

它甚至不必是服务调用服务,等待本地异步方法也使Transaction.Current无效.用一个例子来澄清

[OperationBehavior(TransactionScopeRequired = true)]
public async Task CallAsync()
{
    await WriteAsync();
    // Transaction.Current is now null
    await WriteAsync();                     
}
Run Code Online (Sandbox Code Playgroud)

试图解决方案

我创建了一个Message Inspector,实现了IDispatchMessageInspector并将其作为服务行为附加,代码执行并且没有任何问题,但它与在service方法中声明transactionscope的效果不同.

public class TransactionScopeMessageInspector : IDispatchMessageInspector
{
    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        var transactionMessage = (TransactionMessageProperty)OperationContext.Current.IncomingMessageProperties["TransactionMessageProperty"];
        var scope = new TransactionScope(transactionMessage.Transaction, TransactionScopeAsyncFlowOption.Enabled);            
        return scope;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        var transaction = correlationState as TransactionScope;
        if (transaction != null)
        {
            transaction.Complete();
            transaction.Dispose();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

通过在调试时查看标识符,我可以看到它实际上是在消息检查器中与在服务中 在第一次调用之后相同的事务,即

await _service_WriteAsync();
Run Code Online (Sandbox Code Playgroud)

Transaction.Current变为null.如果没有从消息检查器中的OperationContext.Current获取当前事务,那么同样的事情也是不可能的.

是否有可能实现这一目标?似乎唯一的方法是在服务方法中声明TransactionScope,即:

public async Task CallAsync()
{
    var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
    await _service.WriteAsync();
    await _service.WriteAsync();            
    scope.Complete();
}
Run Code Online (Sandbox Code Playgroud)

如果transaction.current在中间变为null,那么使用以下服务契约显然会在第二个服务调用上获得异常

[OperationContract, TransactionFlow(TransactionFlowOption.Mandatory)]
Task WriteAsync();
Run Code Online (Sandbox Code Playgroud)

Ajd*_*eek 1

事实证明,我们不应该在服务器上将 async/await 关键字与分布式事务一起使用,请参阅此博客文章了解详细信息。