OperationContext is ThreadStatic

Tags: .NET, CodeSnippets, WCF, WinFX, WinFX January CTP

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.

28 Comments

  • Avner Kashtan said

    "tied to the current thread or <b>web request</b>" - I want to have a cross-thread context in my application that is equivalent to the web request context.

  • Avner Kashtan said

    That's a good link - seems to do almost exactly what my code does. Wraps it up a bit more neatly, though.

    The reason I need the context is because it carries some, well, contextual information (requesting user, requesting application and so forth) that I need in my deeper application layers (BL, DAL) to determine what data to return. I can, of course, extract it from the OperationContext object and wrap it in my own object, but then I'd need to pass THAT on other threads.

    Another option is to avoid singleton services altogether (which is good for performance and scalability) and go with per-session service instances. The reason I am currently loathe to do so is both because the service maintains a cache that I don't want to duplicate for each client, and also because the service implements a LCE-style publisher/subscriber model that needs to have a consistent state.

    *sigh*

    At some point I will be forced to split this into two different services. Really don't want to right now, though.

  • dollarjunkie said

    I know this is late but I have a Question. I am a WCF newbie and would at least like to get some explanation of what OperationContext can be used for. from your code above, it does not seem OperationContext is referencing any particular instance of the service object, Is this how it works? void Method () { ThreadPool.QueueUserWorkItem(ThreadMethod, OperationContext.Current); } void ThreadMethod(object state) { OperationContext.Current = state as OperationContext; // Do whatever. }

  • zissubSuifs said

    FGBNFSDGSADSDGASD YUYADFHGDAFADFHAD YUKYADFHGDAFDSFGHADS ZVXZADFHGDAFDSFGHADS QWERSDGSADGADSFHGADFS ASFDSDGSADGDFHAD YUKYASDGASDASDFHGAD ADFHGADFHGDAFADFHGAD

  • Dypeeruse said

    ERYERSDGSADXZCBZX YUKYSDGSADGDSFGHADS YUYADFHGDAFSDGASD ASFDSDGSADASDGHASD GJTRSDGSADXZCBZX YUYADFHGDAFXZCBZX SDGSDZSDGASDXZCBZX GJTRSDGSADGADFHGAD

  • zissubSuifs said

    ZVXZSDGSADDSFGHADS ASFDZSDGASDADFHAD DSGAADFGASDGADFHGAD ASFDSDGSADADFHGAD ZVXZSDGSADDFHAD ERYERSDGSADASDGHASD GJTRSDGSADGADFHGAD ERYERADFGASDGSDFH

  • reriAcroria said

    YUKYSDGSADASDFHGAD YUKYZSDGASDASDFHGAD ADFHGSDGSADGADFHGAD GJTRADFHGDAFSDFH ADFHGSDGSADDFHAD QWERADFHGDAFASDGHASD DSGASDGSADASDGHASD ZVXZSDGSADSDAFHSAD

  • Dypeeruse said

    QWERZSDGASDXZCBZX DSGAASDGASDDFHAD YUYASDGASDSDFH YUYADFGASDGDSFGHADS SDGSDSDGSADGXZCBZX ZVXZSDGSADSDAFHSAD QWERADFHGDAFDFHAD ZVXZSDGSADSDFH

  • groreibia said

    DSGAADFGASDGSDFH FGBNFSDGSADXZCBZX GJTRADFHGDAFSDGASD DSGAADFGASDGASDGHASD ADFHGSDGSADGDSFGHADS FGBNFASDGASDDFHAD DSGASDGSADADSFHGADFS FGBNFSDGSADSDFH

  • Patspeeda said

    SDGSDZSDGASDASDGHASD FGBNFSDGSADASDGHASD ZVXZSDGSADXZCBZX QWERADFGASDGADSFHGADFS SDGSDSDGSADGADFHGAD GJTRADFGASDGSDGASD ADFHGADFHGDAFDSFGHADS QWERSDGSADGADSFHGADFS

  • groreibia said

    YUYSDGSADDFHAD GJTRSDGSADGADFHAD ZVXZSDGSADADFHAD YUYSDGSADGDSFGHADS QWERSDGSADASDGHASD YUKYSDGSADDSFGHADS YUYZSDGASDDFHAD SDGSDSDGSADASDFHGAD

  • woBzmaHyfkQ said

    iTqbhBntgV justin blackmon jerseysjustin blackmon jersey numberjustin blackmon jaguars jerseychris cooley jerseychris cooley jerseychris cooley jerseyblaine gabbert jerseyblaine gabbert jaguars jerseyblaine gabbert jaguars jerseybeanie wells jerseybeanie wells jerseybeanie wells jerseyjohn skelton jerseyjohn skelton jerseyjohn skelton jersey[url]

  • Clay said

    Terrific post however , I was wondering if you could write a litte more on this topic? I'd be very thankful if you could elaborate a little bit further. Bless you!

Comments have been disabled for this content.