6
votes

With C#5 Async-Await in WCF, after an await if rest of the code continues on a different thread, we loose the Current Operation Context. (OperationContext.Current is null).

I am working on a WCF Service which calls another external service. And there are a few Custom Binding Extensions used in the external service call which access the Operation Context. So I need the Context to be propagated during this call and it cant just work with copying the operation context into a local variable.

My config looks like this

<system.serviceModel>
<bindings>
  <customBinding>
<binding name="MyCustomBinding">
      <MyBindingExtention/>
      <security authenticationMode="UserNameOverTransport" />
      <textMessageEncoding maxReadPoolSize="64" >
        <readerQuotas maxStringContentLength="8192" />
      </textMessageEncoding>
      <httpsTransport manualAddressing="false" maxReceivedMessageSize="65536" />
    </binding>
 </customBinding>
 <client>
  <endpoint address="https://ExternalService.svc" binding="customBinding" bindingConfiguration="MyCustomBinding" contract="Contract" name="ExternalService"/>
 </client>
 </bindings> 
<extensions>
 <bindingElementExtensions>
    <add name="MyBindingExtention" type="Bindings.MyBindingExtention, Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  </bindingElementExtensions>  
</extensions>   
</system.serviceModel>

where "MyBindingExtention" accesses the operationcontext to get some information.

public async Task<string> GetExternalData(int value)
{
    var oc = OperationContext.Current;

    //External Web service Call
    var response = await externalService.GetDataAsync();

    return response.text;
}

Is there a good way to make the OperationContext propagate into the external Service Call and then again into the remaining code execution?

1
Thanks Noserratio. I did have a look at your post before. In my case I have an existing service which I am trying to make Async, and I must use the Operation Context. I will just have to wait until Microsoft supports it with Async.user3152534

1 Answers

3
votes

You can use a custom synchronization context. Here's a sample SynchronizationContext implementation:

public class OperationContextSynchronizationContext : SynchronizationContext
{
    private readonly OperationContext context;

    public OperationContextSynchronizationContext(IClientChannel channel) : this(new OperationContext(channel)) { }

    public OperationContextSynchronizationContext(OperationContext context)
    {
        OperationContext.Current = context;
        this.context = context;
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        OperationContext.Current = context;
        d(state);
    }
}

And usage:

var currentSynchronizationContext = SynchronizationContext.Current;
try
{
    SynchronizationContext.SetSynchronizationContext(new OperationContextSynchronizationContext(client.InnerChannel));
    var response = await client.RequestAsync();
    // safe to use OperationContext.Current here
}
finally
{
    SynchronizationContext.SetSynchronizationContext(currentSynchronizationContext);
}