OperationContext is ThreadStatic
WCF Services tend to be big, heavy duty things. When I write a service I often want it to do a lot of work for a lot of clients, and it should do it efficiently. This usually means that I will use multithreaded code to get things done concurrently. Whether using the ThreadPool or instantiating a thread of my own, I expect this is a common scenario for WCF service writers.
That's why I was suprised today to find out that the OperationContext of the current service call, our entry point for getting context information about the current call, its headers and so forth - is marked as [ThreadStatic]. This means that the moment I fork off to another thread, I lose all context information. If I want it available, I have to do it myself.
I don't know how ASP.NET deals with this problem. If I spin a new thread under ASP.NET, I don't lose my current HttpContext. A quick glance with Reflector shows that there's no [ThreadStatic] anywhere. Whatever features of IIS they use there, it's probably unavailable for WCF, so we have to do it manually.
The simplest way to pass the context to a thread is just to send it as a parameter:
void Method ()
{
ThreadPool.QueueUserWorkItem(ThreadMethod, OperationContext.Current);
}
void ThreadMethod(object state)
{
OperationContext.Current = state as OperationContext;
// Do whatever.
}
Side note: Note the automatic delegate inference that .NET 2.0 does, rather than forcing me to manually create a WaitCallback delegate:
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadMethod), OperationContext.Current);
I don't think this was possible in v1.1.
If we want to spin a thread of our own, we can use the ParameterizedThreadStart delegate:
void Method ()
{
Thread t = new Thread(new ParameterizedThreadStart(ThreadMethod), OperationContext.Current);
t.Start();
}
If we have parameters to pass to our method, though, we need to be even hackier - maybe define a struct or class to hold our OperationContext as well as the custom parameters, and pass that on to the ThreadMethod and have it disassemble it.
There is a better way to build a Thread Launcher than can pass the ObjectContext. I'll elaborate on that in my next post.