【问题标题】:Is it possible to create a TransactionScope in a Custom WCF Service Behavior? (async, await, TransactionScopeAsyncFlowOption.Enabled)是否可以在自定义 WCF 服务行为中创建 TransactionScope? (异步、等待、TransactionScopeAsyncFlowOption.Enabled)
【发布时间】:2016-01-13 13:28:10
【问题描述】:

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();
    }
}

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

问题更新

它甚至不必是服务到服务调用,本地异步方法的等待也会使 Transaction.Current 为空。举个例子来说明

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

尝试的解决方案

我创建了一个 Message Inspector,实现了 IDispatchMessageInspector 并将其附加为服务行为,代码执行并且一切都没有问题,但它与在服务方法中声明事务范围的效果不同。

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();
        }
    }
}

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

await _service_WriteAsync();

Transaction.Current 变为 null。如果没有从消息检查器中的 OperationContext.Current 获取当前事务也是一样的,所以这不太可能是问题。

问题

甚至有可能做到这一点吗?看来唯一的方法是在服务方法中声明一个 TransactionScope,即:

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

使用以下服务合同,很明显,如果 transaction.current 在两者之间变为 null,我们会在第二次服务调用中遇到异常

[OperationContract, TransactionFlow(TransactionFlowOption.Mandatory)]
Task WriteAsync();

【问题讨论】:

  • 发现 this book 看起来它提出了与您提出的完全相同的问题并提供了一些解决方案,不幸的是,由于链接是本书的预览,这些解决方案并不完全可用。
  • 它准确地描述了问题,结论基本上是不能以干净的方式完成。引用这本书:“我们认为异步服务操作引入的标准 WCF 行为缺乏奇偶性是 WCF 的设计缺陷......”。然后提出了一个远非理想/疯狂的解决方案。我将坚持手动创建内部事务范围,直到找到更好的东西。感谢您的参考!

标签: c# wcf asynchronous async-await transactionscope


【解决方案1】:

事实证明,我们真的不应该在服务器上将 async/await 关键字与分布式事务一起使用,详情请参阅 this blog post

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-12
    • 2021-06-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多