I read from < Essential ASP.NET with Examples in C# > the following statement:
Another useful property to know about is the static Current property
The backing field of Current is marked as ThreadStatic (I assume), hence it wont be available/initialized in user-created threads.
What it comes down to is, that you should capture the instance of HttpContext.Current in the request thread and then use that instance in your threads, instead of referring to HttpContext.Current.
There are four things working together to cause the behavior you are asking about:
HttpContext.CurrentThread.CurrentThreadThread.CurrentThread is static but references a different Thread object in every threadHttpContext.Current actually points to Thread.CurrentThread.ExecutionContext.IllogicalCallContext.HostContextConclusions we can draw from the above givens:
HttpContext is an instance object and not static we need its reference to access itHttpContext.Current actually points to a property on Thread.CurrentThread, changing Thread.CurrentThread to a different object will likely change HttpContext.CurrentThread.CurrentThread' changes when switching threads, HttpContext.Current also changes when switching threads (in this case HttpContext.Current becomes null).Bringing this all together, what causes HttpContext.Current to not work in a new Thread? The Thread.CurrentThread reference change, which happens when switching threads, changes the HttpContext.Current reference, which prevents us from getting to the HttpContext instance we want.
To reiterate, the only magic thing going on here is Thread.CurrentThread referencing a different object in every Thread. HttpContext works just like any other instance object. Since threads in the same AppDomain can reference the same objects, all we have to do is pass a reference for HttpContext to our new thread. There is no context info to load or anything like that. (there are some fairly serious potential gotchas with passing around HttpContext to other threads but nothing to prevent you from doing it).
A few final side notes I came across while researching:
In some cases a Thread's ExecutionContext is 'flowed' (copied) from one Thread to another. Why then is HttpContext not 'flowed' to our new Thread? Because HttpContext doesn't implement the ILogicalThreadAffinative interface. A class stored in the ExecutionContext is only flowed if it implements ILogicalThreadAffinative.
How does ASP.NET move HttpContext from Thread to Thread (Thread-Agility) if it isn't flowed? I'm not entirely sure, but it looks like it might pass it in HttpApplication.OnThreadEnter().
I think I've found a suitable explanation here: http://odetocode.com/articles/112.aspx
To summarize, the code-behind of HttpContext.Current looks like this:
public static HttpContext get_Current()
{
return (CallContext.GetData("HtCt") as HttpContext);
}
with CallContext functioning as thread-local storage (i.e. each thread will see a different copy of the data and cannot access the copies seen by other threads). Hence, once the current context is initialized on one thread, subsequent accesses by other threads will result in a NullReferenceException, since the property is thread-local to the initial thread.
So yes, your initial explanation was close, in the sense that it is a problem of data being visible only to a single thread.