Why is HttpContext.Current null after await?

I have the following test WebAPI code, I don't use WebAPI in production but I made this because of a discussion I had on this question: WebAPI Async question

Anyways, here's the offending WebAPI method:

public async Task<string> Get(int id)
{
    var x = HttpContext.Current;
    if (x == null)
    {
        // not thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    await Task.Run(() => { Task.Delay(500); id = 3; });

    x = HttpContext.Current;
    if (x == null)
    {
        // thrown
        throw new ArgumentException("HttpContext.Current is null");
    }

    return "value";
}

I had hereto believed that the second exception is expected because when the await completes, it will likely be on a different thread where HttpContext.Current as a thread-static variable will no longer resolve to the appropriate value. Now, based on the synchronization context, it could actually be forced to go back to the same thread after the await but I'm not doing anything fancy in my test. This is just a plain, naive use of await.

In comments in another question I was told that HttpContext.Current should resolve after an await. There's even another comment on this question indicating the same. So what's true? Should it resolve? I think no, but I want an authoritative answer on this because async and await is new enough that I can't find anything definitive.

TL;DR: Is HttpContext.Current potentially null after an await?


Solution 1:

Please ensure you are writing an ASP.NET 4.5 application, and targeting 4.5. async and await have undefined behavior on ASP.NET unless you are running on 4.5 and are using the new "task-friendly" synchronization context.

In particular, this means you must either:

  • Set httpRuntime.targetFramework to 4.5, or
  • In your appSettings, set aspnet:UseTaskFriendlySynchronizationContext to true.

More information is available here.

Solution 2:

As @StephenCleary correctly pointed out, you need this in your web.config:

<httpRuntime targetFramework="4.5" />

When I was first troubleshooting this, I did a solution-wide search for the above, confirmed it was present in all my web projects and quickly dismissed it as the culprit. Eventually it occurred to me to look at those search results in full context:

<!--
  For a description of web.config changes for .NET 4.5 see http://go.microsoft.com/fwlink/?LinkId=235367.

  The following attributes can be set on the <httpRuntime> tag.
    <system.Web>
      <httpRuntime targetFramework="4.5" />
    </system.Web>
-->

Doh.

Lesson: If you upgrade a web project to 4.5, you still need to get that setting in place manually.

Solution 3:

I ran into this issue recently. As Stephen pointed out not setting explicitly the target framework can generate this issue.

In my case, our Web API was migrated to version 4.6.2 but the runtime target framework was never specified in the web config, so basically this was missing inside the <system.web> tag:

If you have doubts about the framework version you are running this may help: Add the following line on any of your Web API methods and set a breakpoint to verify what type is currently loaded at runtime and verify it is not a Legacy implementation:

You should see this (AspNetSynchronizationContext):

enter image description here

Instead of LegazyAspNetSynchronizationContext (Which was what I saw before adding the target framework):

enter image description here

If you go to the source code (https://referencesource.microsoft.com/#system.web/LegacyAspNetSynchronizationContext.cs) you will see that the Legacy implementation of this interface lacks of asynchronous support.

enter image description here

I spent a lot of time trying to find the source of the issue and Stephen´s response helped a lot. Hope this answer provides some more information about the issue.

Solution 4:

Is my test flawed, or is there some web.config element I'm missing here that would make HttpContext.Current resolve correctly after an await?

Your test is not flawed and HttpContext.Current should not be null after the await because in ASP.NET Web API when you await, this will ensure that the code that follows this await is passed the correct HttpContext that was present before the await.