asp.net mvc 4.5:让 HostingEnvironment.QueueBackgroundWorkItem 开始工作

asp.net mvc 4.5: getting HostingEnvironment.QueueBackgroundWorkItem to work

我正在尝试完成一些轻量级任务在控制器 完成他的工作之后,我为此使用 HostingEnvironment.QueueBackgroundWorkItem()

我看到了一个奇怪的行为,所以我为此制作了一个人工概念验证应用程序。

在我的 global.asax.cs 中,我有这个很好的功能:

public class MvcApplication : HttpApplication
{
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    public static void ContinueWorkOnBackground(Task workItem)
    {
        workItem.ContinueWith(t =>
        {
            if (t.IsFaulted)
            {
                var ex = t.Exception;
                logger.Error(ex);
            }
        });
        HostingEnvironment.QueueBackgroundWorkItem(_ => workItem);
    }

然后在我的控制器中创建一个工作项并将其扔到那里:

public ActionResult About()
{
    logger.Info("About");
    ViewBag.Message = "Your application description page.";
    MvcApplication.ContinueWorkOnBackground(TestWorkItem());
    return View();
}

private async Task TestWorkItem()
{
    logger.Trace("TestWorkItem");
    await Task.Delay(500);
    logger.Trace("let's fail");
    throw new NotImplementedException();
}

我在日志中看到消息 "TestWorkItem",但从来没有 "let's fail",也没有错误消息。

我也做了 HostingEnvironment.QueueBackgroundWorkItem((Func<CancellationToken,Task>)(_ => workItem)); 以确保调用没有歧义。

我该如何解决这个问题?

您的问题是 TestWorkItem 正在捕获当前请求上下文,并试图在其 await 之后在该请求上下文上恢复。 (我在我的博客上解释 how this capture/resume works)。由于请求已完成,请求上下文在它尝试恢复时已不存在。

最好的解决方法是允许 QueueBackgroundWorkItem 在请求上下文之外执行代码。因此,首先更改您的辅助方法以采用 Func<Task> 而不是 Task:

public static void ContinueWorkOnBackground(Func<Task> workItem)
{
  HostingEnvironment.QueueBackgroundWorkItem(async _ =>
  {
    try
    {
      await workItem();
    }
    catch (Exception ex)
    {
      logger.Error(ex);
    }
  });
}

那么你可以这样称呼它:

public ActionResult About()
{
  logger.Info("About");
  ViewBag.Message = "Your application description page.";
  MvcApplication.ContinueWorkOnBackground(() => TestWorkItem());
  return View();
}