等待异步方法在结果需要之前不返回
我无法解决我遇到异步/等待的问题。
简而言之,我有一个控制器,装饰有属性。 此属性从i / o密集型进程(filesystem / db / api等…)获取指定的内容
然后,它将返回的内容设置为ViewBag上的Dictionary
然后,在视图中,我可以做这样的事情,以检索内容:
@(ViewBag.SystemContent["Common/Footer"])
我遇到的问题是,它第一次运行时,内容没有返回,并且通过字符串索引检索值的调用失败,因为它不存在。
点击F5,没关系。
控制器动作非常简单:
[ProvideContent("Common/Footer")] public class ContactDetailsController : Controller { public async Task Index() { //omitted for brevity - awaits some other async methods return View(); } }
属性
public override async void OnActionExecuting(ActionExecutingContext filterContext) { if (filterContext.Result is ViewResult) { var localPath = filterContext.RouteData.Values["controller"] + "/" + filterContext.RouteData.Values["action"]; if (!_useControllerActionAsPath) localPath = _path; var viewResult = filterContext.Result as ViewResult; //this seems to go off and come back AFTER the view requests it from the dictionary var content = await _contentManager.GetContent(localPath); if (viewResult.ViewBag.SystemContent == null) viewResult.ViewBag.SystemContent = new Dictionary(); viewResult.ViewBag.SystemContent[localPath] = new DisplayContentItem(content, localPath); }
编辑
更改属性中的以下行:
var content = await _contentManager.GetContent(localPath);
至
var content = Task.Factory.StartNew(() => manager.GetContent(localPath).Result, TaskCreationOptions.LongRunning).Result;
解决了这个问题,但我觉得这与我在Stephen Clearys博客上读到的所有内容相违背……
我不是100%熟悉ASP.Net MVC堆栈以及所有这些是如何工作的,但我会对它进行一次尝试。
OnActionExecuting()
文档说:
在调用action方法之前调用。
由于您覆盖了以前的同步方法并使其异步,因此期望代码完整,并为下一个执行步骤做好准备。
理论执行路径:
public void ExecuteAction() { OnActionExecuting(); OnActionExecution(); OnActionExecuted(); }
由于你覆盖了OnActionExecuting()
方法,执行堆栈基本上仍然是相同的,但是下一个要执行的代码( ExecuteAction()
和OnActionExecuted()
以及任何名为ExecuteAction()
)都期望进行同步调用,所以据他们所知,一切都很好,并准备继续运行。
基本上,这归结为OnActionExecuting()
不是一个异步方法,没有任何期望它。 (它在MVC 6中也不是异步的。)
在OnActionExecuting()
和它的顺序调用之后,同步调用的东西是引用viewResult.ViewBag.SystemContent
,因此它没有获得你想要的值。 正如你在标题中所说的那样,
等待异步方法在结果需要之前不返回。
使用任务的“捕获”是您不能保证任务何时完成,但保证它将完成。
潜在解决方案
- 将
GetContent()
调用移出该事件。 - 存储为
GetContent()
创建的任务,找到使用viewResult.ViewBag.SystemContent
的下一个位置,并检查任务完成情况或等待完成。 -
为GetContent()方法添加超时间隔。(执行此操作的方式有很多种.MSDN Docs for Task类这不会解决您的问题。
编辑:用于在控制器中存储任务的代码示例
[ProvideContent("Common/Footer")] public class ContactDetailsController : Controller { /* * BEGINNING OF REQUIRED CODE BLOCK */ private Task _getContentForLocalPathTask; private string _localPath; /* * END OF REQUIRED CODE BLOCK */ public async Task Index() { //omitted for brevity - awaits some other async methods /* * BEGINNING OF REQUIRED CODE BLOCK */ string content = await _getContentForLocalPath; viewResult.ViewBag.SystemContent[_localPath] = new DisplayContentItem(content, _localPath); /* * END OF REQUIRED CODE BLOCK */ return View(); } public override async void OnActionExecuting(ActionExecutingContext filterContext) { if (filterContext.Result is ViewResult) { var localPath = filterContext.RouteData.Values["controller"] + "/" + filterContext.RouteData.Values["action"]; if (!_useControllerActionAsPath) localPath = _path; var viewResult = filterContext.Result as ViewResult; /* * BEGINNING OF REQUIRED CODE BLOCK */ _localPath = localPath; _getContentForLocalPathTask = _contentManager.GetContent(localPath); /* * END OF REQUIRED CODE BLOCK */ if (viewResult.ViewBag.SystemContent == null) viewResult.ViewBag.SystemContent = new Dictionary(); } }
- 如何取消等待Task.Delay()?
- TaskCancellationException如何避免成功控制流程中的exception?
- 在循环内部,每个异步调用是否使用task的continuewith链接到返回的任务?
- SendMailAsync:在异步操作仍处于挂起状态时完成的异步模块或处理程序
- 如何使用AsyncPump在异步控制台应用程序中保留exception上下文?
- Winforms调用异步方法挂起程序
- 一个代码示例说明了async / await和Reactive(Rx)扩展的范例之间的区别?
- Windows Phone 8异步等待使用
- TaskContinuationOptions.RunContinuations异步和Stack Dives