WCF + TransactionScopeRequired + async + throw fault => ProtocolException "transaction under which method was executing was asynchronously aborted"

WCF + TransactionScopeRequired + async + throw fault => ProtocolException "transaction under which method was executing was asynchronously aborted"

在使用异步主机实现(在我的例子中使用任务)并异步抛出异常(即从延续)的服务上调用标有 "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.DistributedIdentifier验证)。

此代码已使用 .NET Framework 4.7.2 进行测试。

到目前为止,我的研究表明此 ProtocolException 是由服务主机上的 WCF 管道抛出的(从异常中的堆栈跟踪中也可以看出),并且据推测是为了处理在主机的外部中止的分布式事务控制(即当 DTC 发出中止时)。虽然这看起来有些道理,但这里显然不是这种情况。

有没有办法在主机上仍然使用异步实现的同时避免此 ProtocolException?

Is there any way to avoid this ProtocolException while still using an async implementation on the host?

看来您不应该在服务宿主实现中创建自己的 TransactionScope 实例,而应仅依赖于 WCF 管道创建的 TransactionScope。

我相应地调整了上面的服务主机实现代码并且 ProtocolException 消失了(我现在在客户端上得到了 FaultException)。

this post 的评论让我走上了正确的轨道。