在异步任务中使用HttpContext

我有以下mvc动作。

public async Task 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的解决方案?

在我们的Io​​cContainer中,我们注册了以下对象,该对象将用户名传递给框架。

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

在持久化到数据库之前,在很多地方调用此代码。

我们需要另一种方法来获取用户的用户名启动webrequest或修复HttpContext为null的问题。

因为在任务中对数据库的持久化发生在进入任务之前无法访问HttpContext。

我也想不出一个临时持久保存用户名的安全方法,所以我可以实现另一个ICredentials服务对象。

您几乎不想在ASP.NET方法中使用Task.Run

我认为最干净的解决方案(但最多的工作)是在其他层实现async兼容的接口:

 public async Task DoSomeLongRunningOperation() { //Do a lot of long running stuff var intermediateResult = await DoLongRunningStuff(); return await DetermineFinalResult(intermediateResult); } 

我会尝试将对HttpContext的引用作为状态对象传递,因为这应该在堆栈上为执行工作的线程创建该对象的新实例。 而不是使用Task.Run,​​使用

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

Task.Run和Task.Factory.StartNew立即返回,因此当您的线程在已经处置的对象上操作时,asp.net继续处理正在处理请求的工作线程中的事件生命周期。

启动新线程之前,您应该从当前上下文中获取所需的任何信息。 在这种情况下,添加如下内容:

 string username = HttpContext.Current.User.Username; 

Task.Run之前,然后在另一个线程内部使用它。

另外,就目前而言,没有理由await任务。 您可以直接返回任务,而不是将方法标记为Async

如果你需要访问Response对象,它可能会利用长时间运行的结果,因此不能在Task.Run之前,你应该在Task.Run 之后这样做(但确保任务await编辑) 。 如果你最终这样做,那么你不能做我在前一段中建议的。