none
Alternative to the OperationContext when using async/await RRS feed

  • Question

  • I'm hosting my .NET 4.5 WCF services in IIS. There's a piece of information called "BusinessContext" (BC) that is stored in the OperationContext.Current instance, so that any logic downstream can reach it.

    Everything worked fine until I introduced async/await, and I ran into this issue. @stephen-cleary mentioned ASP.NET uses the async-friendly AspNetSynchronizationContext to keep the HttpContext.Current across threads. Since I'm hosting in IIS I figured I should be able to take advantage of the AspNetSyncCtx in WCF, and use the HttpContext.Current instead of the OperationContext to store the BC.

    I created a WCF service from scratch, which has targetFramework = 4.5, aspnet:UseTaskFriendlySynchronizationContext = true and aspNetCompatibilityEnabled = true set by default in the Web.config. I also added the AspNetCompatibilityRequirements = Required to my service.

    At runtime I see the HttpContext.Current is there, but SynchronizationContext.Current is null. After an await the HttpContext becomes null, which is expected because there's no SyncCtx. Shouldn't it be set to AspNetSyncCtx when aspcompatibility is required? How does the AspNetSyncCtx get set in ASP.NET?

    Thanks in advance


    Friday, August 30, 2013 2:51 PM

All replies

  • My guess is, it depends on what your client-side thread environment for making async WCF call is. It should not be a problem if you called them from a UI thread, because in that case you'd be guaranteed that await continuation doesn't switch threads. 

    In the above case, I'd assume you call your WCF proxy from a Windows service (could be a console app, a child or random pool thread). It this case, the Default (ThreadPool) SynchronizationContext context is used, where a thread switch indeed may occur after await, thus breaking the OperationContextScope object which is not designed for multi-threaded access ([EDITED]: we clarified, it is called from ASP.NET Web API code, so AspNetSynchronizationContext is used here).

    [EDITED]
    To stick with the convinient 
    using pattern for OperationContextScope, it appears you actually need to set your own custom SynchronizationContext which would provide a behavior similar to DispatcherSynchronizationContext for await continuation, before making an async call. I'm not sure if it is a feasible solution. I need to dig into this a little more, hopefully someone can give a better answer.

    [UPDATE]
    The this-guy-did-just-what-MSDN-says code you referenced appears to be just wrong, because most likely, the new 
    OperationContext would get created and disposed of before the WCF async call task might even have a chance to start:

      var docClient = CreateDocumentServiceClient();
      using (new OperationContextScope(docClient.InnerChannel))
      {
        return docClient.GetDocumentAsync(docId);
      }

    I briefly looked at OperationContext implementation using Reflector. It stores its state on a per-thread basis by design. Which in my opinion means, regardless of the 
    OperationContextScope wrapper, if you really need to use OperationContext, you have to come with a custom SynchronizationContext implementation to avoid thread switching.

    [UPDATE] We're getting close, now that we know the calling environment is ASP.NET. Indeed, AspNetSynchronizationContext doesn't necessarily provide the same thread for async task completion callbacks (and I just learnt that while reading thisthis and re-reading Stephen's article). That make sense, otherwise it would eventually block the calling thread (when there is no more async work), instead of returning it to the pool.

    I think your decision to get rid of OperationContextScope (and not depend on HTTP headers) is probably the best way to handle the situation. I believe you can sill utilize using or try/catch/finally to call Close or Abort on your WCF proxy, because its code is thread-safe and thread-independent (unlike OperationContext).

    Still waiting for an answer or comments from authoritative sources.    
    Monday, September 2, 2013 6:22 AM
  • Thanks for your reply Diana191.

    Actually I'm not worried about the client side because it happens to be a 4.5 ASP .NET website, which already has the async-await friendly AspNetSyncCtx. The problem is on the service side, where I have 4.5 WCF services hosted in IIS.

    I think the most elegant solution is implementing a custom SyncCtx to flow the OperationContext from thread to thread, but I think that's opening a can of worms. I don't want to go down that (scary) route.

    Now, considering I'm hosting in IIS with ASP.NET compatibility enabled my common sense tells me there should be a way to reuse the AspNetSyncCtx from WCF. Now, that class is internal so I can't just create an instance and inject it into the WCF pipeline. Is there a way to achieve that?


    Tuesday, September 3, 2013 4:59 PM
  • Any thoughts on this?
    Thursday, September 5, 2013 3:04 PM
  • Finally I found a way to obtain the AspNetSyncCtx from ASP.NET and inject it into the WCF pipeline via an IContractBehavior, but it didn't work. It gets stuck and the async WCF method never gets called. I feel I'm trying to fit a circle into a square.

    At this point I'm back to my original concern... OperationContext is nulled out after an await. This question is for the WCF team:

    Is there any chance this will be fixed in the upcoming release of the framework?

    If the answer is no, can you shed some light on how to implement a custom SyncContext that preserves the OperationContext across threads, similar to AspNetSyncCtx/HttpContext?

    Monday, September 9, 2013 10:50 PM
  • I am running into the same kinds of major problems with WCF and async .. running in ASP.NET compatibility HttpContext, OperationContext and (since I am using it in SharePoint) SPContext are all null.

    Is there some other solution than passing all these context objects all the way down my call chain?

    Monday, September 9, 2013 11:42 PM
  • I think I found a possible solution, read here for the specifics.
    Tuesday, September 10, 2013 8:45 PM
  • I've been testing the code I posted on SO and unfortunately it doesn't work. The OperationContext is null after an await.

    Is there any official word or workaround for this issue?

    Wednesday, September 18, 2013 4:29 AM
  • I managed to get the operation context marshalled across async await calls without having to pass it through or wrap calls in using (new OperationContextScope(docClient.InnerChannel)). It involved postsharp and IL. I had to create a custom aspect that literally rewrote the async state machine that gets created to add additional logic to marshal the OperationContext across.  

    My best advice would be to avoid any sort of context and try and find other ways to solve whatever problem you have. Until MS fixes this WCF async await bug.

    Edit: I was actually hoping that this would have been fixed in 4.5.1(Is it?) and that is why I googled it and found this thread.
    Wednesday, October 23, 2013 7:30 AM
  • Diana191, I'm delighted you have copied my post from StackOverflow literally without any changes.
    Thursday, November 7, 2013 12:27 AM
  • Tersius, are you using a custom awaiter to flow OperationContext.currentContext (which is declared in v4.5.1 as [ThreadStatic] private static OperationContext.Holder currentContext)? 

    Maybe you should report it as a bug: http://connect.microsoft.com/VisualStudio/, although I have doubts it will ever get fixed.


    Thursday, November 7, 2013 12:53 AM