6

In the following example, the method is exposed as a WCF service operation and the service is hosted in IIS. On entry into the function the WebOperationContext.Current is set as expected. After the await has finished waiting, however, the WebOperationContext.Current is set to null.

        public async Task TestAsync()
    {
        //WebOperationContext.Current is set

        Task t = new Task(()=>Thread.Sleep(10000));

        t.Start();

        await t;

        //WebOperationContext.Current is null
    }

This would seem to be a shortcoming, so I wondered if anyone was aware of this and if there were any good ways around it. I realise I could cache a reference to the conext in a local variable, but this doesn't seem great.

Update

One approach that works is

            public async Task TestAsync()
    {
        WebOperationContext ctx = WebOperationContext.Current;

        Task t = new Task(()=>Thread.Sleep(10000));

        t.Start();

        await t;

        //ctx is set        
    }

and also, as others have hinted at, I could do this

        public async Task TestAsync()
    {
        CallContext.LogicalSetData("WebOperationContext.Current", WebOperationContext.Current);

        Task t = new Task(()=>Thread.Sleep(10000));

        t.Start();

        await t;

        WebOperationContext ctx = (WebOperationContext)CallContext.LogicalGetData("WebOperationContext.Current");
    }

What would be the implications in terms of performance and thread safety of each?

Justin Harvey
  • 14,446
  • 2
  • 27
  • 30

1 Answers1

5

I've heard the WCF team is considering solutions for OperationContext.Current, and I expect they will address WebOperationContext.Current as well. See this SO post (note that Jon Cole is on the MS WCF team).

In the meantime, you can capture the value in a variable (which improves testability, and is what I recommend), add it to LogicalCallContext, or install your own SynchronizationContext (which would be tricky since you're hosted in IIS which uses its own SynchronizationContext).

Community
  • 1
  • 1
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I had seen that post, but hadn't realised JC was on the team, hence I thought I'd ask again. It would seem like a good candidate for fixing, though am not sure what trickery would be involved, given await implies the following code block will execute on a different thread. – Justin Harvey Nov 08 '12 at 14:57
  • The complexity comes in because WCF is host-neutral. `await` will capture and resume on a "context", but this is usually `SynchronizationContext`, which is determined by the host and not WCF (and has no "hooks" for inserting your own data to flow with the context, other than `LogicalCallContext`, which would have a fairly significant performance hit if the WCF team just used it all the time and also may flow over remote calls and into other appdomains). So I don't know *how* they're going to solve it. ;) – Stephen Cleary Nov 08 '12 at 15:05
  • I'd be interested to hear what you thought the best way to capture the value in a variable would be, is it safe just to store a local reference and use this or should I do more? – Justin Harvey Nov 08 '12 at 16:26
  • It's safe to use a local (or instance) variable; I would expect that to be faster than `LogicalCallContext`. But I would consider a refactoring that would remove `WebOperationContext` from `TestAsync` completely - IoC, possibly using DI. That way your `TestAsync` is easily testable. – Stephen Cleary Nov 08 '12 at 16:42