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.
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);
}
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.
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 await
ed). If you end up doing this then you can't do what I suggested in my previous paragraph.