15

我有以下 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 为空。我们做了很多欺骗,但没有任何东西可以保证 HttpContext 在我们的新线程中始终可用。

是否有在异步任务中使用 HttpContext 的解决方案?

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

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

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

我们需要另一种方法来获取发起 web 请求的用户的用户名,或者解决 HttpContext 为空的问题。

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

我也想不出一种安全的方法来临时保留用户名,这样我就可以实现另一个 ICredentials 服务对象。

4

3 回答 3

5

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

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

public async Task<JsonResult> DoSomeLongRunningOperation()
{
  //Do a lot of long running stuff
  var intermediateResult = await DoLongRunningStuff();
  return await DetermineFinalResult(intermediateResult);
}
于 2012-12-06T17:46:30.620 回答
4

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

string username = HttpContext.Current.User.Username;

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

await顺便说一句,就目前而言,这项任务没有理由。您可以直接返回任务而不将方法标记为Async.

如果您需要访问该Response对象,这可能会利用长时间运行的操作的结果,因此不能在您应该在之后这样Task.Run做(但请确保该任务已编辑)。如果您最终这样做,那么您将无法执行我在上一段中建议的操作。Task.Runawait

于 2012-12-06T16:44:08.393 回答
3

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

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

Task.Run 和 Task.Factory.StartNew 立即返回,因此 asp.net 在处理请求的工作线程中的事件生命周期中继续运行,而您的线程正在对已释放的对象进行操作。

于 2012-12-06T18:06:44.483 回答