3

As discussed nicely here and here, you should use HttpContext.Current.Items rather than a ThreadStatic variable to store data for an ASP.NET request, since your request might be processed by more than one thread.

How about if you want to do something on a background thread? Is it safe to use ThreadStatic data and to set Thread.CurrentPrincipal.Identity to avoid having to push 'global' data through all your methods? I'm assuming that it's fine, but would like to be sure. For instance, I'm not explicitly using any await in the code executed on a background thread but libraries I'm calling probably are; might ASP.NET use my thread to do other things while such async code is not active? If that's the case, what's a safe way to set some 'thread-static' data and user information on a thread before running it?

I'm well aware of the pitfalls of running code on background threads in ASP.NET. But sometimes you've gotta do what you've gotta do. In my case we're not yet on .NET 4.5.2 so can't use QueueBackgroundWorkItem, so we're using Nito.AspNetBackgroundTasks to run the background tasks. This internally uses Task.Run().

For example, I'd do something like this:

public class SomeController
{    
    public void AControllerMethod()
    {
        // Get the data set earlier on the HttpContext
        var theData = HttpContext.Current.Items["importantKey"];
        var thePrincipal = HttpContext.Current.User;
        // Pass it to the background thread method
        BackgroundTaskManager.Run(() => DoStuffInBackground(theData, thePrincipal));        
    }

    private void DoStuffInBackground(string someData, IPrincipal user)
    {
        // Store the data & principal for use by logic classes later
        Thread.CurrentPrincipal = user;
        MyContextService.BackgroundData["importantKey"] = someData;

        // do some things, some of which would call:
        aLogicClass.DoSomethingDeepInLogicCode();
    }    
}

public class ExampleLogicClass
{    
    private void DoSomethingDeepInLogicCode()
    {
        // get the context info
        var user = Thread.CurrentPrincipal;
        var theData = MyContextService.GetContextData("importantKey");

        // .. do stuff with user & data....        
    }    
}

public static class MyContextService
{     
    [ThreadStatic]
    public static ConcurrentDictionary<string,object> BackgroundData = new ConcurrentDictionary<string,object>();

    // Gets data from HttpContext or from BackgroundData 
    public object GetContextData(string key)
    {
        if (HttpContext.Current != null)
        { 
            var data = HttpContext.Current.Items[key]; 
            if (data != null) return data;
        } 
        if (BackgroundData.ContainsKey(key))
        {
            return BackgroundData[key];
        }
        return null;
    }    
} 

UPDATE:

From a bit more reading I guess I should use CallContext.LogicalSetData() and LogicalGetData() to set/get the equivalent of ThreadLocal/ThreadStatic data. (Or AsyncLocal<T> in .net 4.6).

More clarification from someone who knows this stuff properly would be great.

Community
  • 1
  • 1
Rory
  • 40,559
  • 52
  • 175
  • 261

0 Answers0