代码之家  ›  专栏  ›  技术社区  ›  Gilles Hemberg

WCF+TransactionScopeRequired+async+throw fault=>ProtocolException“执行方法的事务已异步中止”

  •  0
  • Gilles Hemberg  · 技术社区  · 7 年前

    在使用异步主机实现(在我的示例中使用任务)并异步引发异常(即从延续引发)的服务上调用标记为“TransactionScopeRequired=true”的WCF操作,似乎总是会导致客户端接收到以下异常(而不是我引发的异常):

    System.AggregateException: One or more errors occurred. ---> System.ServiceModel.ProtocolException: The transaction under which this method call was executing was asynchronously aborted.
       at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
       at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
       at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
       at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass6_0.<CreateTask>b__0(IAsyncResult asyncResult)
       at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
       --- End of inner exception stack trace ---
       at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
       at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
       at System.Threading.Tasks.Task.Wait()
       at WcfTransactionScopeRequiredAsync.Program.Main(String[] args) in D:\\WcfTransactionScopeRequiredAsync\\WcfTransactionScopeRequiredAsync.Client\\Program.cs:line 19
    ---> (Inner Exception #0) System.ServiceModel.ProtocolException: The transaction under which this method call was executing was asynchronously aborted.
       at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
       at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
       at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
       at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass6_0.<CreateTask>b__0(IAsyncResult asyncResult)
       at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)<---
    

    服务合同:

    [ServiceContract]
    public interface IService
    {
        [FaultContract(typeof(SomeFault))]
        [TransactionFlow(TransactionFlowOption.Allowed)]
        [OperationContract]
        Task DoItAsync();
    }
    

    [OperationBehavior(TransactionScopeRequired = true)]
    public async Task DoItAsync()
    {
        using (TransactionScope tx = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
        {
            await Task.Delay(1000).ConfigureAwait(continueOnCapturedContext: false);
            throw new FaultException<SomeFault>(new SomeFault());
            tx.Complete();
        }
    }
    

    客户端是同步的:

    service.DoItAsync().Wait();
    

    在这种情况下,客户端不流动事务(因此这肯定不是分布式事务(通过检查transaction.Current.TransactionInformation.DistributeIdentifier进行验证)。

    这段代码已经过测试。NET框架4.7.2。

    到目前为止,我的研究表明,此ProtocolException是由服务主机上的WCF管道引发的(从异常中的堆栈跟踪中也可以看出),应该用于处理在主机控制之外中止的分布式事务(即DTC发出中止时)。虽然这似乎有点道理,但这里显然不是这样。

    在主机上仍然使用异步实现时,是否有任何方法可以避免此ProtocolException?

    0 回复  |  直到 7 年前
        1
  •  0
  •   Gilles Hemberg    7 年前

    在主机上仍然使用异步实现时,是否有任何方法可以避免此ProtocolException?

    似乎您不应该在服务主机实现中创建自己的TransactionScope实例,而应该完全依赖WCF管道创建的TransactionScope。

    我相应地修改了上面的服务主机实现代码,ProtocolException消失了(我现在在客户机上得到了FaultException)。

    评论 this post 让我走上正轨。