当客户端服务事务在服务端中止并且没有引发错误时,WCF客户端是否会抛出协议异常?

时间:2012-12-02 12:35:07

标签: c# wcf distributed-transactions

我有一个现有的Web服务,其中不支持事务流,并在响应的错误集合中返回错误而不是抛出错误。 所以响应消息合同如下所示:

[MessageContract()]
public class UpdateResponse
{
    [MessageBodyMember()]
    public UpdateData Data { get; set; }

    [MessageBodyMember()]
    public ErrorMessages Errors { get; set; }        
}

现在将更新此服务,以便其操作可以包含在分布式事务中,该事务将在客户端启动。 因为它没有抛出错误,所以我启用了事务流并将transactionAutoComplete设置为false。

因此,在操作实现中,我将仅在错误集合为空时手动提交事务:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, 
            ConcurrencyMode = ConcurrencyMode.Single,
            TransactionIsolationLevel = IsolationLevel.ReadCommitted)]
public partial class MyService: MyServiceContract
{
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    public UpdateResponse Update(UpdateRequest request)
    {
        UpdatePlanResponse response = new UpdateResponse();

        ... call inner components to perform operation ...

        if (errors.Count == 0)
        {
            OperationContext.Current.SetTransactionComplete();
        }
        return response;
    }
}

在客户端,我还在wsHttpbinding配置中启用了事务流。 正在这样的事务范围内调用该服务:

    using (TransactionScope tran = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions{ IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted}))
    {
        UpdateResponse response = _serviceClient.Update(updateRequest);

        ... some more work ...

        if(response.Errors != null && response.Errors.Count > 0)
        {
            ... handle and raise them ...
        }
        tran.Complete();
    }

交易流向服务。在积极的情况下,所有工作都按预期工作,服务代码所做的更改仅在客户端提交事务时保留。 现在,如果在服务上发现任何问题,服务将中止事务(因为已创建内部事务范围并且在该方案中它将无法完成)并且错误集合中具有多个错误的响应消息将是发回来。

但是在客户端,一个ProtocolException指出“正在执行此方法调用的事务是异步中止的”。被WCF引发:

  

服务器堆栈跟踪:      在System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(消息回复,MessageFault错误,字符串操作,MessageVersion版本,FaultConverter faultConverter)      在System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime操作,ProxyRpc& rpc)      在System.ServiceModel.Channels.ServiceChannel.Call(String action,Boolean oneway,ProxyOperationRuntime operation,Object [] ins,Object [] outs,TimeSpan timeout)      在System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall,ProxyOperationRuntime操作)      在System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

     

在[0]处重新抛出异常:      在System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg,IMessage retMsg)      在System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData,Int32 type)      在MyServiceContract.Update(UpdateRequest请求)      在MyServiceContractClient.Update(UpdateRequest请求)

在这种情况下我能做些什么,所以我可以在服务端中止事务,同时仍然向客户端发送一些有关出错的信息而不引发错误?

抛出错误在这里会更有意义(并且避免我启用会话,这是将事务自动完成设置为false所需的),但由于它是现有服务,我想在使用之前检查我的选项。

更新

如果执行服务操作,如下例所示,包含错误信息的响应将到达客户端。我可以看到事务状态为已中止但WCF没有抛出ProtocolException:

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    public UpdateResponse Update(UpdateRequest request)
    {
        UpdatePlanResponse response = new UpdateResponse();

        response.errors = new List<Error>() { new Error("dummy"); }
        if (response.errors.Count == 0)
        {
            OperationContext.Current.SetTransactionComplete();
        }
        return response;
    }

但是,如果我在服务实现上启动另一个事务范围并且它被中止,则会将错误发送回将引发ProtocolException的客户端:

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    public UpdateResponse Update(UpdateRequest request)
    {
        UpdatePlanResponse response = new UpdateResponse();

        //start another transaction scope and abort it
        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
        {
        }

        response.errors = new List<Error>() { new Error("dummy"); }
        if (response.errors.Count == 0)
        {
            OperationContext.Current.SetTransactionComplete();
        }
        return response;
    }

似乎在第二种情况下,WCF尝试中止已经中止的事务,这会导致以下错误被发送回客户端...

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:r="http://schemas.xmlsoap.org/ws/2005/02/rm" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
     ...
  </s:Header>
  <s:Body>
    <s:Fault>
      <s:Code>
        <s:Value>s:Sender</s:Value>
        <s:Subcode>
          <s:Value xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:TransactionAborted</s:Value>
        </s:Subcode>
      </s:Code>
      <s:Reason>
        <s:Text xml:lang="en-GB">The transaction under which this method call was executing was asynchronously aborted.</s:Text>
      </s:Reason>
    </s:Fault>
  </s:Body>
</s:Envelope>

启用跟踪我可以看到在服务端为System.ServiceModel源记录了以下异常(由于此异常,上面的错误被发送到客户端):

<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error">
    <TraceIdentifier>http://msdn.microsoft.com/en-GB/library/System.ServiceModel.Diagnostics.TraceHandledException.aspx</TraceIdentifier>
    <Description>Handling an exception.</Description>
    <AppDomain>a0ef2bea-25-129990861717638246</AppDomain>
    <Exception>
        <ExceptionType>System.ServiceModel.FaultException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
        <Message>The transaction under which this method call was executing was asynchronously aborted.</Message>
        <StackTrace>
            at System.ServiceModel.Diagnostics.ExceptionUtility.TraceHandledException(Exception exception, TraceEventType eventType)
            at System.ServiceModel.Dispatcher.TransactionInstanceContextFacet.CheckIfTxCompletedAndUpdateAttached(MessageRpc&amp; rpc, Boolean isConcurrent)
            at System.ServiceModel.Dispatcher.TransactionBehavior.ResolveOutcome(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ResolveTransactionOutcome(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage9(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage8(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc&amp; rpc)
            at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
            at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
            at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
            at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
            at System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(IAsyncResult result)
            at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
            at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
            at System.ServiceModel.Channels.ReceiveTimeoutAsyncResult.Callback(IAsyncResult result)
            at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
            at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
            at System.Runtime.InputQueue`1.AsyncQueueReader.Set(Item item)
            at System.Runtime.InputQueue`1.Dispatch()
            at System.ServiceModel.Channels.ReliableReplySessionChannel.ProcessSequencedMessage(RequestContext context, String action, WsrmSequencedMessageInfo info)
            at System.ServiceModel.Channels.ReliableReplySessionChannel.ProcessRequest(RequestContext context, WsrmMessageInfo info)
            at System.ServiceModel.Channels.ReliableReplySessionChannel.ProcessDemuxedRequest(RequestContext context, WsrmMessageInfo info)
            at System.ServiceModel.Channels.ReliableReplyListenerOverReply.ProcessSequencedItem(ReliableReplySessionChannel reliableChannel, RequestContext context, WsrmMessageInfo info)
            at System.ServiceModel.Channels.ReliableListenerOverDatagram`4.HandleReceiveComplete(TItem item, TInnerChannel channel)
            at System.ServiceModel.Channels.ReliableListenerOverDatagram`4.OnTryReceiveComplete(IAsyncResult result)
            at System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(IAsyncResult result)
            at System.ServiceModel.Diagnostics.TraceUtility.&lt;&gt;c__DisplayClass4.&lt;CallbackGenerator&gt;b__2(AsyncCallback callback, IAsyncResult result)
            at System.Runtime.AsyncResult.Complete(Boolean completedSynchronously)
            at System.Runtime.InputQueue`1.AsyncQueueReader.Set(Item item)
            at System.Runtime.InputQueue`1.EnqueueAndDispatch(Item item, Boolean canDispatchOnThisThread)
            at System.Runtime.InputQueue`1.EnqueueAndDispatch(T item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
            at System.ServiceModel.Channels.SingletonChannelAcceptor`3.Enqueue(QueueItemType item, Action dequeuedCallback, Boolean canDispatchOnThisThread)
            at System.ServiceModel.Channels.HttpChannelListener.HttpContextReceived(HttpRequestContext context, Action callback)
            at System.ServiceModel.Activation.HostedHttpTransportManager.HttpContextReceived(HostedHttpRequestAsyncResult result)
            at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.HandleRequest()
            at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.BeginRequest()
            at System.ServiceModel.Activation.HostedHttpRequestAsyncResult.OnBeginRequest(Object state)
            at System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
            at System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
            at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
        </StackTrace>
        <ExceptionString>System.ServiceModel.FaultException: The transaction under which this method call was executing was asynchronously aborted.</ExceptionString>
    </Exception>
</TraceRecord>

0 个答案:

没有答案