16

I have the following mvc action.

public async Task<JsonResult> DoSomeLongRunningOperation()
{
    return await Task.Run(() =>
    {
        //Do a lot of long running stuff
        //The underlying framework uses the HttpContext.Current.User.Identity.Name so the user is passed on the messagebus.
    }
}

In the task the HttpContext gets null. We did a lot of tricking, but nothing assures us of the HttpContext being available always in our new thread.

Is there a solution to use HttpContext within out async tasks?

In our IocContainer we have registered the following object which passes the username to the framework.

public class HttpContextUserIdentityName : ICredentials
{
    public string Name
    {
        get { return HttpContext.Current.User.Identity.Name; }
    }
}

This code is called in a lot of places before persisting to the database.

We need either another way of getting the username of the user initiated the webrequest or fix the issue with the HttpContext being null.

Because the persisting to the database happens in the Task I can't access the HttpContext before entering the task.

I also can't think of a safe way to temporary persist the username so I can implement another ICredentials service object.

1

3 Answers 3

5

You almost never want to use Task.Run in an ASP.NET method.

I think the cleanest solution (but the most work) is to implement async-compatible interfaces at your other layers:

public async Task<JsonResult> DoSomeLongRunningOperation()
{
  //Do a lot of long running stuff
  var intermediateResult = await DoLongRunningStuff();
  return await DetermineFinalResult(intermediateResult);
}
Sign up to request clarification or add additional context in comments.

5 Comments

Ofcourse that code is in different layer of the architecture, but that is not the problem here. I would still have the same issue if I would define that in different class.
Really bad answer. There are many APIs that require Task.
@TheSharpNinja: I'll stand by my answer. If you need a Task, then use async/await, not Task.Run.
That's not possible if the function being called must return void. Using async void causes problems with exception handling. Using Task.Run is the correct technique in such cases. Now, I have been giving this a lot of thought and using a custom scheduler that associates threads to context objects would solve the problem.
@TheSharpNinja: Perhaps you could expound more on your scenario and post your own question? Task.Run does not sound like a good solution for that case.
4

You should get whatever information you need from the current context before you start the new thread. In this case, add something like:

string username = HttpContext.Current.User.Username;

before Task.Run and then use that inside of the other thread.

On a side note, as it stands, there's no reason to await the task. You can just return the task directly and not mark the method as Async.

If you need to access the Response object, which will presumably to utilize the results of the long running operation and thus can't be before Task.Run you should do so after the Task.Run (but ensure that the task is awaited). If you end up doing this then you can't do what I suggested in my previous paragraph.

4 Comments

I updated my question... Need to access the context within the task or need another safe solution for getting my username before saving stuff to the database.
@MarcoFranssen You won't be able to access the current context from a background thread, it's just not going to happen. As I have already said, you need to get the information from the current context before starting the background thread. The other issue might be that you are doing more than you should be in the background task. Perhaps sections of this method should be refactored out entirely to run else where, or only a smaller section that actually interacts with the DB or does some processing should be set to run in a background task. The problem here is mixing business and UI code.
@Servy You can actually close over the HttpContext object as the state object for a new task. This should create a new copy of that object on the stack for the new thread. See answer below.
@hotSauce.Open I've listed several of the problems with that in a comment to your thread; that aside, you're not actually closing over it in your code, you're just passing it in as a formal parameter. That's...different.
3

I would try passing in the reference to the HttpContext as the state object, because that should create a new instance of that object on the stack for the thread that executes the work. Instead of using Task.Run, use

return await Task.Factory.StartNew((ctx) => 
{
    var context = (HttpContext)ctx;
   //Do stuff
}, httpContextObject);

Task.Run and Task.Factory.StartNew return immediately, so asp.net continues on in the event lifecycle in the worker thread that is handling the request while your thread is operating on the object that has already been disposed.

4 Comments

First off, you're not creating a copy of the object, you're just copying the reference to the object. HttpContext isn't a struct. Next, there's a reason it's not accessible from a background thread, and that reason is that it was not designed to be accessed from other contexts. It's not a thread safe object by design, you can only rely on the current state of the object when you're in the appropriate sync context, etc. You're simply not supposed to access the HttpContext from another thread, in just the way that in a winform app you don't access controls from a non-UI thread.
@Servy According to this similar question and answer, you can close over the HttpContext. If each thread gets it's own stack and the HttpContext is passed to that thread as a parameter, then a copy of that object should get created on the heap for the new thread.
Sure you can close over it. I never said you couldn't I said you shouldn't. Yes, each thread gets it's own stack, I'm not disputing that. What I am disputing is that what you're doing is in any way copying the context; it's not. While the stacks differ (by necessity) the heap is shared. Since HttpContext is a reference type, the actual object sits on the heap. You are only copying a reference to that object in your closure.
Ok, this may not be the answer for the above question, but it was useful for me, thanks! :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.