5

I encountered a weird issue today which made no sense to me. Here is a summary:

Inside a method, I check for a cached item as below:

private async Task<RatesStatus> getRatesStatusAsync() {

    //...

    if (_currentHttpContext != null) {

        //Here, I am checking for a Cached item
        var cachedRatesStatusObj = HttpContext.Current.Cache[Constants.RATESSTATUS_CACHE_KEY_NAME];
        if (cachedRatesStatusObj != null)
            return (RatesStatus)cachedRatesStatusObj;
    }

    //...

    cacheRatesStatusObject(ratesStatus);

    //...
}

Here, the HttpContext.Current is not null as expected inside an ASP.NET application. Then, inside the cacheRatesStatusObject method, I check if HttpContext.Current is null or not as below:

private void cacheRatesStatusObject(RatesStatus ratesStatus) {

    //...

    //Seeing if HttpContext.Current is null or not first.
    //and it is null here...
    if (HttpContext.Current == null)
        return;

    //...
}

And it is null there. No idea what is happening here. Any thoughts?

tugberk
  • 57,477
  • 67
  • 243
  • 335
  • 1
    I noted your method is marked `async`. Just to be sure, wherever you are calling this method, you are doing an `await` somewhere before the `Response` is sent back to the client and closed, right? Otherwise it could just be a race condition, where the `Response` is being returned and disposed before getting to this line, but still available at the line above. – Mike Guthrie May 11 '12 at 15:31
  • @GuthMD Yes, I used `await` somewhere inside the method. This must be the problem. – tugberk May 11 '12 at 16:02

3 Answers3

4

When you use async/await, the thread handling the request marks the request as incomplete and then returns to the ASP.NET thread pool. When the awaitable completes later, another thread is assigned to run the rest of the method, however HttpContext is not migrated across threads, that's why you get null reference when calling await method.

You can pass a reference of the HttpContext to the await method, something like this:

await cacheRatesStatusObject(HttpContext.Current,  ratesStatus);

However you should be very careful dealing with concurrency and race conditions, for example if the await thread locks a resource and another request thread attempts to use it then your thread pool goes boom. Most people resolve this by creating new objects and passing them into paramaterized threads instead of passing a reference of HttpContext across threads.

Kamyar Nazeri
  • 25,786
  • 15
  • 50
  • 87
  • Actually `HttpContext.Current` is not null after await. It is only null in the async method. See this question: http://aspnetwebstack.codeplex.com/discussions/359012 – Aliostad Jun 09 '12 at 23:35
1

Passing the instance sucks.

Use the .NET 4 MemoryCache classes instead.

http://stevescodingblog.co.uk/net4-caching-with-mvc/

Micah
  • 10,295
  • 13
  • 66
  • 95
0

It does not null itself.

The HttpContext is only stored in a 'thread static' way.

As suggested by the other answer, just pass the instance.

leppie
  • 115,091
  • 17
  • 196
  • 297
  • Yep, I just figured that out. I get an instance of it before the async call: `var httpContext = HttpContext.Current;` then later in the process, I work with this local variable. – tugberk Jun 03 '12 at 15:19
  • This does not make much sense. Why thread context does not get copied?? – Aliostad Jun 09 '12 at 16:55
  • @Aliostad: It is just the way it works. Else other threads serving other requests would share the same state. – leppie Jun 09 '12 at 16:58
  • @leppie This works on windows. At the time of context switching, all the thread-storage-area data gets copied. This include HttpContext.Current. I cannot understand how this could work differently. Also at any time, only one thread serves a request so there is no problem with that. – Aliostad Jun 09 '12 at 17:21