-
Notifications
You must be signed in to change notification settings - Fork 571
Description
My service using ConcurrencyMode.Multiple and my Framework code is identical to NET code.
NET code sometimes throws SFxCallbackRequestReplyInOrder1 exceptions in System.ServiceModel.Channels.ServiceChannel on client side.
While debugging my Framework code, I see that OperationContext.Current is always null and anyway ConcurrencyMode.Single, in NET code OperationContext.Current is not null and this results in an exception.
wcf/src/System.ServiceModel.Primitives/src/System/ServiceModel/Channels/ServiceChannel.cs
Lines 526 to 541 in 9bd5ad4
| private void PrepareCall(ProxyOperationRuntime operation, bool oneway, ref ProxyRpc rpc) | |
| { | |
| OperationContext context = OperationContext.Current; | |
| // Doing a request reply callback when dispatching in-order deadlocks. | |
| // We never receive the reply until we finish processing the current message. | |
| if (!oneway) | |
| { | |
| DispatchRuntime dispatchBehavior = ClientRuntime.DispatchRuntime; | |
| if ((dispatchBehavior != null) && (dispatchBehavior.ConcurrencyMode == ConcurrencyMode.Single)) | |
| { | |
| if ((context != null) && (!context.IsUserContext) && (context.InternalServiceChannel == this)) | |
| { | |
| throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.SFxCallbackRequestReplyInOrder1, typeof(CallbackBehaviorAttribute).Name))); | |
| } | |
| } | |
| } |
This led me to this section of code where the OperationContext.Current value is assigned.
wcf/src/System.ServiceModel.Primitives/src/System/ServiceModel/Dispatcher/MessageRpc.cs
Lines 397 to 475 in 9bd5ad4
| internal bool Process(bool isOperationContextSet) | |
| { | |
| using (ServiceModelActivity.BoundOperation(Activity)) | |
| { | |
| bool completed = true; | |
| if (NextProcessor != null) | |
| { | |
| MessageRpcProcessor processor = NextProcessor; | |
| NextProcessor = null; | |
| OperationContext originalContext; | |
| if (!isOperationContextSet) | |
| { | |
| originalContext = OperationContext.Current; | |
| } | |
| else | |
| { | |
| originalContext = null; | |
| } | |
| IncrementBusyCount(); | |
| try | |
| { | |
| if (!isOperationContextSet) | |
| { | |
| OperationContext.Current = OperationContext; | |
| } | |
| processor(ref this); | |
| if (!IsPaused) | |
| { | |
| OperationContext.SetClientReply(null, false); | |
| } | |
| } | |
| catch (Exception e) | |
| { | |
| if (Fx.IsFatal(e)) | |
| { | |
| throw; | |
| } | |
| if (!ProcessError(e) && FaultInfo.Fault == null) | |
| { | |
| Abort(); | |
| } | |
| } | |
| finally | |
| { | |
| try | |
| { | |
| DecrementBusyCount(); | |
| if (!isOperationContextSet) | |
| { | |
| OperationContext.Current = originalContext; | |
| } | |
| completed = !IsPaused; | |
| if (completed) | |
| { | |
| channelHandler.DispatchDone(); | |
| OperationContext.ClearClientReplyNoThrow(); | |
| } | |
| } | |
| catch (Exception e) | |
| { | |
| if (Fx.IsFatal(e)) | |
| { | |
| throw; | |
| } | |
| throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(e.Message, e); | |
| } | |
| } | |
| } | |
| return completed; | |
| } | |
| } |
I noticed that the Framework code uses
OperationContext.Holder instead of OperationContext.Current