トランザクション フローがサポートされていない既存の 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. System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage メソッド呼び出し、ProxyOperationRuntime 操作) で System.ServiceModel.Channels.ServiceChannel.Call(文字列アクション、ブール一方向、ProxyOperationRuntime 操作、Object[] ins、Object[] outs、TimeSpan タイムアウト)。 ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage メッセージ)
[0] で例外が再スローされました: System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg) で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;
}
2 番目のケースのように、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& rpc, Boolean isConcurrent)
at System.ServiceModel.Dispatcher.TransactionBehavior.ResolveOutcome(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ResolveTransactionOutcome(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage9(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage8(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& 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.<>c__DisplayClass4.<CallbackGenerator>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>