我有以下mvc动作。
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.
}
}
在任务中,HttpContext变为null。我们做了很多尝试,但没有任何事情让我们确信HttpContext总是在我们的新主题中可用。
是否有在异步任务中使用HttpContext的解决方案?
在我们的IocContainer中,我们注册了以下对象,该对象将用户名传递给框架。
public class HttpContextUserIdentityName : ICredentials
{
public string Name
{
get { return HttpContext.Current.User.Identity.Name; }
}
}
在持久保存到数据库之前,会在很多地方调用此代码。
我们需要另一种方法来获取用户的用户名启动webrequest或修复HttpContext为null的问题。
因为在任务中发生了对数据库的持久化,所以在进入任务之前我无法访问HttpContext。
我也想不出一个临时持久保存用户名的安全方法,所以我可以实现另一个ICredentials服务对象。
答案 0 :(得分:5)
您几乎不想在ASP.NET方法中使用Task.Run
。
我认为最干净的解决方案(但最多的工作)是在其他层实现async
- 兼容接口:
public async Task<JsonResult> DoSomeLongRunningOperation()
{
//Do a lot of long running stuff
var intermediateResult = await DoLongRunningStuff();
return await DetermineFinalResult(intermediateResult);
}
答案 1 :(得分:4)
我会尝试将对HttpContext的引用作为状态对象传递,因为这应该在堆栈上为执行工作的线程创建该对象的新实例。而不是使用Task.Run,使用
return await Task.Factory.StartNew((ctx) =>
{
var context = (HttpContext)ctx;
//Do stuff
}, httpContextObject);
Task.Run和Task.Factory.StartNew立即返回,因此当您的线程在已经处理的对象上操作时,asp.net继续处理正在处理请求的工作线程中的事件生命周期。
答案 2 :(得分:3)
在启动新线程之前,您应该从当前上下文获取所需的任何信息。在这种情况下,添加如下内容:
string username = HttpContext.Current.User.Username;
在Task.Run
之前,然后在另一个帖子中使用它。
另一方面,就目前而言,await
没有理由Async
这项任务。您可以直接返回任务,而不是将方法标记为Response
。
如果你需要访问Task.Run
对象,这可能是为了利用长时间运行的结果,因此不能在Task.Run
之前你应该这样做之后 await
(但确保任务为{{1}})。如果你最终这样做,那么你就不能按照我在前一段中的建议去做。