在异步任务中使用HttpContext

时间:2012-12-06 16:38:51

标签: c# asp.net-mvc asp.net-mvc-3 asynchronous async-await

我有以下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的解决方案?

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

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

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

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

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

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

3 个答案:

答案 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}})。如果你最终这样做,那么你就不能按照我在前一段中的建议去做。