New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wcf:disableOperationContextAsyncFlow flag set to true by default #403

Closed
jonreis opened this Issue May 15, 2017 · 3 comments

Comments

Projects
None yet
2 participants
@jonreis

jonreis commented May 15, 2017

Our application has been working fine until we upgraded to .NET 4.7. Now it appears that the OperationContext.Current is null after performing async/await calls.

The OperationContext.Current continues to be set properly on .NET 4.6.2 and lower, just not on machines that do not have .NET 4.7 installed.

I see in ServiceModelAppSettings that

DefaultDisableOperationContextAsyncFlow = true;

Is this this a breaking change in .NET 4.7?

BTW, my guess is that this is something developers would want to opt out of, not in.

@jonreis

This comment has been minimized.

jonreis commented May 15, 2017

I can confirm that setting the following in our app.config file, fixed the problem for us.

 <appSettings>
    <add key="wcf:disableOperationContextAsyncFlow" value="false" />
  </appSettings>
@jcondex

This comment has been minimized.

jcondex commented May 24, 2017

Hi Jonreis,

The default value of the appSettings switch is correct and it is indeed a breaking change. I strongly encourage you to use the default behavior of 4.7 and work around the OperationContext.Current being null after an async/await call by doing something like this:

public async Task Test()
{
OperationContext currentContext = OperationContext.Current;
await Task.Delay(100); // this is the await call.
//use currentContext instead of OperationContext.Current
}

In .NET 4.6.2 we added support for WCF’s OperationContext.Current to correctly flow with the ExecutionContext in asynchronous continuations.

Previously, the internal implementation of OperationContext.Current was to store the Current context using a ThreadStatic variable, which used the thread's local storage (TLS) to store the data associated with Current context. The problem with this is if there was a change in the execution context of the method call (i.e. a thread change caused by awaiting another operation), any subsequent calls to OperationContext.Current would result in null being returned. The way we implemented the feature was to rely on AsyncLocal to provide the persisting the value of the OperationContext across different threads.

There are currently are two known issues around this feature that break old scenarios.

Issue #1: Users might obtain a NULL result when calling OperationContext.Current

This issue occurs when one creates an OperationContextScope and proceeds to call OperationContext.Current within the using clause. A common pattern that is prone to this failure is the following:

using (new OperationContextScope(OperationContext.Current))
{
OperationContext context = OperationContext.Current; //OperationContext.Current returns null
// ...
}

To work around this issue, one needs to change the code to something similar to the following:

OperationContext ocx = OperationContext.Current;
using (new OperationContextScope(OperationContext.Current))
{
OperationContext.Current = new OperationContext(ocx.Channel);
// ...
}

Issue #2: Users might run into a deadlock when using Reentrant services.
This issue occurs when one creates a Reentrant service – which restricts instances of the service to one thread of execution at a time. Users prone to running into this problem will have the following ServiceBehaviorAttribute in their code:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]

To work around this issue, users should set the ConcurrencyMode of their service to ConcurrencyMode.Single or ConcurrencyMode.Multiple, and provide the appropriate synchronization primitives to ensure a single thread can execute a single method at a time.

We have rolled out an update of System.ServiceModel.dll and we strongly encourage users to patch their existing .NET 4.6.2 framework. The patch fixes Issue #1, disables the flow of OperationContext.Current by default, and provides an Opt-In option to enables feature.

To enable the flow of OperationContext.Current across multiple threads in async operations, use the following AppSettings key:

Setting the value of Switch.System.ServiceModel.DisableOperationContextAsyncFlow to true will disable the flow of the ExecutionContext in OperationContext.Current and will effectively revert the behavior to pre-4.6.2, which is the default behavior after this patch. Setting the value to false will enable the feature and should not be used in Reentrant services.

@jonreis

This comment has been minimized.

jonreis commented May 29, 2017

Thank you for the information jcondex.

We will just have to be careful with our customers that are currently running our product on .NET 4.6.2 because if they upgrade to 4.7 without first upgrading our product, it will fail with NREs due to this change.

We do not use ConcurrencyMode.Reentrant, so we should be good setting disableOperationContextAsyncFlow to false in the App.config.

@jonreis jonreis closed this May 29, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment