Async WCF client calls with custom headers: This OperationContextScope is being disposed out of order

12,916

Solution 1

Everything seems to work quite well with the following code:

public async void TestMethod()
{
    var result = await CallServerAsync();
}

public Task<Result> CallServerAsync()
{
    var address = new EndpointAddress(url);
    var client = new AdminServiceClient(endpointConfig, address);

    using (new OperationContextScope(client.InnerChannel))
    {
        OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = GetHeader();

        var request = new MyRequest(...); 
        {
            context = context,
        };

        return client.GetDataFromServerAsync(request);
    }
}

Solution 2

According to Microsoft documentation:

Do not use the asynchronous "await" pattern within a OperationContextScope block. When the continuation occurs, it may run on a different thread and OperationContextScope is thread specific. If you need to call "await" for an async call, use it outside of the OperationContextScope block.

So the simplest proper solution is:

Task<ResponseType> task;
using (new OperationContextScope(client.InnerChannel))
{
    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = GetHeader();

    var request = new MyRequest(...); 
    {
        context = context,
    };

    task = client.GetDataFromServerAsync(request);
}

var result = await task;

Solution 3

This is a known "issue" and for anyone stuck with this, you can simply run your call synchronously. Use GetAwaiter().GetResult(); instead since it doesn't schedule a Task at all, it simply blocks the calling thread until the task is completed.

public Result CallServer()
{
    var address = new EndpointAddress(url);
    var client = new AdminServiceClient(endpointConfig, address);

    using (new OperationContextScope(client.InnerChannel))
    {
        OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = GetHeader();

        var request = new MyRequest(...); 
        {
            context = context,
        };

        return client.GetDataFromServerAsync(request).GetAwaiter().GetResult();
    }
}
Share:
12,916
Mikael Koskinen
Author by

Mikael Koskinen

Mikael is .NET and Azure developer, Blazor enthusiast and the founder of Adafy Oy, a Finnish software development house.

Updated on July 05, 2022

Comments

  • Mikael Koskinen
    Mikael Koskinen almost 2 years

    I'm calling a WCF service from a WinRT app. The service requires that some headers are set for the authentication. The problem is that if I do multiple calls to the service simultaneously, I get the following exception:

    This OperationContextScope is being disposed out of order.

    The current code looks like the following:

    public async Task<Result> CallServerAsync()
    {
        var address = new EndpointAddress(url);
        var client = new AdminServiceClient(endpointConfig, address);
    
        using (new OperationContextScope(client.InnerChannel))
        {
            OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = GetHeader();
    
            var request = new MyRequest(...); 
            {
                context = context,
            };
    
            var result = await client.GetDataFromServerAsync(request);
        }
    }
    

    I found the following comment from the docs:

    Do not use the asynchronous “await” pattern within a OperationContextScope block. When the continuation occurs, it may run on a different thread and OperationContextScope is thread specific. If you need to call “await” for an async call, use it outside of the OperationContextScope block.

    So it seems I'm clearly calling the service incorrectly. But what is the correct way?

  • Ramon de Klein
    Ramon de Klein over 7 years
    This might work, but it will work by coincidence. Because you don't await the GetDataFromServerAsync call the thread-switch won't occur. The operation's context-scope is already disposed before the call has actually completed. The reason it works is probably because the outgoing header is added before the inner call awaits internally.
  • sich
    sich about 7 years
    You can await the task GetDataFromServerAsync returns right after the end of the using block (assign the task to a variable).
  • supertopi
    supertopi about 5 years
    @sich but in that case the client does not use the custom message properties.
  • sich
    sich about 5 years
    @supertopi why not, you initiate the operation in the using block, where all configuration is captured, and then only wait for completion outside of it.
  • Salvuccino
    Salvuccino over 2 years
    This seems to work as expected.
  • Caner
    Caner almost 2 years
    you've saved my day.