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();
}
我正在尝试完成一些轻量级任务在控制器 完成他的工作之后,我为此使用 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();
}