我无法解决我遇到异步/等待的问题。
简而言之,我有一个控制器,用属性装饰。 此属性从i / o密集型进程(文件系统/ db / api等...)
获取指定的内容然后,它将返回的内容设置为ViewBag上的Dictionary<string, string>
然后,在视图中,我可以做这样的事情来检索内容:
@(ViewBag.SystemContent["Common/Footer"])
我遇到的问题是,它第一次运行时,内容没有返回,并且通过字符串索引检索值的调用失败,因为它不存在。<登记/> 点击F5,没关系。
控制器动作非常简单:
[ProvideContent("Common/Footer")]
public class ContactDetailsController : Controller
{
public async Task<ActionResult> 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<string, MvcHtmlString>();
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博客上读到的所有内容相违背...
答案 0 :(得分:8)
我并不是100%熟悉ASP.Net MVC堆栈以及这一切是如何工作的,但我会对它进行一次尝试。
在调用action方法之前调用。
由于您覆盖了以前的同步方法并使其异步,因此它希望代码完整,并为下一个执行步骤做好准备。
理论执行路径:
public void ExecuteAction()
{
OnActionExecuting();
OnActionExecution();
OnActionExecuted();
}
由于您覆盖OnActionExecuting()
方法,执行堆栈基本上仍然相同,但下一个要执行的代码(ExecuteAction()
和OnActionExecuted()
以及任何名为ExecuteAction()
的代码)他们希望能够进行同步通话,所以据他们所知,一切都很好并准备好继续运行。
基本上,归结为OnActionExecuting()
不是异步方法,没有任何期望它。 (它在MVC 6中也不是异步的。)
在OnActionExecuting()
及其连续通话后同步调用的某些内容正在引用viewResult.ViewBag.SystemContent
,因此它无法获得您的值{&#39}。想要的。正如你在标题中所说的那样,
等待异步方法在结果出现之前不返回。
&#39;捕获&#39;使用“任务”是指您无法保证 任务完成,但保证 完成。
潜在的解决方案:
GetContent()
来电移出该事件。GetContent()
创建的任务,找到您viewResult.ViewBag.SystemContent
使用的下一个位置,并检查任务是否完成或等待完成。编辑:用于在控制器中存储任务的代码示例
[ProvideContent("Common/Footer")]
public class ContactDetailsController : Controller
{
/*
* BEGINNING OF REQUIRED CODE BLOCK
*/
private Task<string> _getContentForLocalPathTask;
private string _localPath;
/*
* END OF REQUIRED CODE BLOCK
*/
public async Task<ActionResult> 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<string, MvcHtmlString>();
}
}