2

We have a worker queue that a user can add work to. When the worker item is added the context is the users (HttpContext). But its a background thread that polls the queue and executes the items one by one in order.

I cant just store the User because when the HttpContext is disposed so will the Principal object

The code that can run in the worker needs the Principal to be correct for stuff like PrincipalPermissions etc.

Also, Lifetime management (IoC) uses the HttpContext for InRequest scopes, is it possible to recreate a HttpContext with the correct principal etc.

edit: Faking HttpContext is just a nice to have feature for Life time management, this I can work around. But our backend code heavily depends on having the correct user principal for the thread since we use this to validate if user has access to that part of the system. I would mark as answer if someone can answer how to store a user principal with identity, roles and IsAuthenticated state and later use that on another thread

Anders
  • 17,306
  • 10
  • 76
  • 144

3 Answers3

1

Your best practice for consuming stateful data from the HttpContext is to create your own application specific context which accepts an HttpContext in the constructor (Dependency Injected).

Your business logic should never be dependent on an HttpContext but rather your new application specific context (which may have been created using info from an HttpContext).

This will not only solve your above problems, but also increase testability of your code.

Example:

public class MyApplicationContext
{
    public IPrincipal ContextPrincipal { get; set; }

    public MyApplicationContext(HttpContext httpContext)
    {
        // Store the current user principal & identity
        ContextPrincipal = httpContext.User;

        // Need to grab anything else from the HttpContext? Do it here! 
        // That could be cookies, Http request header values, query string 
        // parameters, session state variables, etc.
        //
        // Once you gather up any other stateful data, store it here in 
        // your application context object as the HttpRequest can't be passed 
        // to another thread.
    }

}

public class MyHttpHandler : IHttpHandler
{
    #region IHttpHandler Members

    public bool IsReusable
    {
        // Return false in case your Managed Handler cannot be reused for another request.
        // Usually this would be false in case you have some state information preserved per request.
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        // Do some work on another thread using the ThreadPool
        ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), new MyApplicationContext(context));
    }

    public void DoWork(object state)
    {
        // Grab our state info which should be an instance of an 
        // MyApplicationContext.
        MyApplicationContext context = (MyApplicationContext) state;

        // Assign this ThreadPool thread's current principal according 
        // to our passed in application context.
        Thread.CurrentPrincipal = context.ContextPrincipal;

        // Check if this user is authenticated.
        if (context.ContextPrincipal.Identity.IsAuthenticated)
        {
            var userName = context.ContextPrincipal.Identity.Name;
        }

        // Check if this user is an administrator.
        if (context.ContextPrincipal.IsInRole("Administrator"))
        {
        }

        // Do some long-ish process that we need to do on the threadpool 
        // after the HttpRequest has already been responded to earlier.
        //
        // This would normally be some fancy calculation/math, data 
        // operation or file routines.
        for (int i = 0; i < 30; i++)
        {
            Thread.Sleep(1000);
        }
    }

    #endregion
}

Neither the IPrincipal nor IIdentity interface explicitly offer a dispose method. So they should both be ok to keep a reference to them. However, I haven't tested the above code, I wrote it just for this question.

If by some poor design they actually do depend on an underlying database connection to query the roles membership, you'd simply have to evaluate that earlier in the constructor of your application context while the HttpContext and asp.net forms authentication provider are still non disposed/closed.

You can always take apart the principal and identity and recreate a new instance of GenericPrincipal and GenericIdentity or even create your application Identity class which implements IIdentity. There is lots of room for customization/extension here.

BenSwayne
  • 16,810
  • 3
  • 58
  • 75
  • The problem is that its the code in your thread sleep loop thats dependent on the Principal and the principal will fail once the HttpContext is disposed. To clarify our business logic does not have any what so ever dependency to System.Web classes. But it uses the Thread.CurrentPrincipal which under the hood uses the HttpContext (If the code is run under IIS). – Anders Jun 03 '13 at 07:04
  • @Anders: Review the code again. The first thing I did above is to assign the `Thread.CurrentPrincipal` in the new ThreadPool thread! I also explained that the principal itself is not disposed - the only possible explanation is that the underlying provider which populates the principal's roles list is disposed not allowing you to check roles. Maybe you should post a stack trace of the exception you get? Eitherway just do these check's earlier and store in your application context - or write your own IIdentity to store more info. – BenSwayne Jun 03 '13 at 15:06
0
public void TestMethod1()
{
    System.Net.WebClient client = new System.Net.WebClient();
    client.BaseAddress = "http://www.teejoo.com";            

    //Invoke your function here
    client.OpenReadAsync(new Uri("http://www.teejoo.com/YourLogicalPage.aspx"));
    //Pur your logical in your page, so you can use httpContext 

    client.OpenReadCompleted += new System.Net.OpenReadCompletedEventHandler(client_OpenReadCompleted);
}

void client_OpenReadCompleted(object sender, System.Net.OpenReadCompletedEventArgs e)
{            
    //to Check the response HERE
}
Eugene
  • 325
  • 1
  • 5
  • This is not what Im after, I do not want to create a http request, I want to create a httpcontext for a background worker that does not have one. Actually whats most important is that I can somehow store and reuse the HttpContext's Principal – Anders May 30 '13 at 06:46
  • you can not create a HttpContext in background, so you can cross over this problem by creating a httpRequest and visiting the page which in your application – Eugene May 31 '13 at 06:34
  • and there is a HttpContext in your logical page as you will. – Eugene May 31 '13 at 06:35
  • why you are trying to clone a Principal object, you can start your new 'faking httpcontext' by using the principal object that the user have provided, start your background work in 'THIS' request thread, and set your client.principal = this.request.principal – Eugene Jun 01 '13 at 04:15
  • You cant because that principal is disposed when the HttpContext is – Anders Jun 01 '13 at 13:02
0

Why don't you use an auxiliar class to hold the information you need? You can create it during the web request with the appropriate values and pass it down as an argument to the background worker.

Cloning the HTTPContext object is not possible because of the internal server session state. Even if it were possible, using it outside of a real HTTP request just to check for values doesn't seem like a good solution.

Community
  • 1
  • 1
nmat
  • 7,430
  • 6
  • 30
  • 43
  • The minimum requirement is that the Principal for the thread is correct, because we use this to validate method access using PrincipalAttributes. So if I somehow can clone the principal with identity, roles and IsAuthenticated state that would be good enough – Anders May 30 '13 at 11:23