由于MVC 4不支持异步子操作(通过
Html.Action),我正在寻找一种强制同步执行子操作的方法.解决此限制的一个简单方法是提供所有控制器操作的同步版本:
public class FooAsyncController : Controller { public async Task<ActionResult> IndexAsync() { var model = await service.GetFoo().ConfigureAwait(false); return View(model); } } public class FooSyncController : FooAsyncController { public ActionResult Index() { return IndexAsync().Result; // blocking call } }
但是,由于我们允许对所有控制器操作执行子操作请求,因此对每个控制器执行此操作都是真正的PITA.
在框架中是否有任何可扩展性点,我们可以检查操作的返回值,如果它返回任务< T>我们正在处理儿童行动,强制同步通话?
解决方法
在几小时内浏览ASP.NET MVC源代码之后,我能够提出的最佳解决方案(除了创建每个控制器操作的同步版本之外)是手动调用Controller中异步Action方法的操作描述符. HandleUnkNownAction.
我对这段代码并不是特别满意,我希望它可以改进,但确实有效.
这个想法是故意请求一个无效的操作(前缀为“_”),它将调用控制器上的HandleUnkNownAction方法.在这里,我们寻找匹配的异步操作(首先从actionName中删除下划线)并调用AsyncActionDescriptor.BeginExecute方法.通过立即调用EndExecute方法,我们可以有效地同步执行操作描述符.
public ActionResult Index() { return View(); } public async Task<ActionResult> Widget(int page = 10) { var content = await new HttpClient().GetStringAsync("http://www.foo.com") .ConfigureAwait(false); ViewBag.Page = page; return View(model: content); } protected override void HandleUnkNownAction(string actionName) { if (actionName.StartsWith("_")) { var asyncActionName = actionName.Substring(1,actionName.Length - 1); RouteData.Values["action"] = asyncActionName; var controllerDescriptor = new ReflectedAsyncControllerDescriptor(this.GetType()); var actionDescriptor = controllerDescriptor.FindAction(ControllerContext,asyncActionName) as AsyncActionDescriptor; if (actionDescriptor != null) { AsyncCallback endDelegate = delegate(IAsyncResult asyncResult) { }; IAsyncResult ar = actionDescriptor.BeginExecute(ControllerContext,RouteData.Values,endDelegate,null); var actionResult = actionDescriptor.EndExecute(ar) as ActionResult; if (actionResult != null) { actionResult.ExecuteResult(ControllerContext); } } } else { base.HandleUnkNownAction(actionName); } }
风景
<h2>Index</h2> @Html.Action("_widget",new { page = 5 }) <!-- note the underscore prefix -->
我几乎可以肯定通过重写Controller.BeginExecute有更好的方法.默认实现如下所示.我的想法是立即执行Controller.EndExecuteCore,尽管到目前为止我没有取得任何成功.
protected virtual IAsyncResult BeginExecute(RequestContext requestContext,AsyncCallback callback,object state) { if (disableAsyncSupport) { // For backwards compat,we can disallow async support and just chain to the sync Execute() function. Action action = () => { Execute(requestContext); }; return AsyncResultWrapper.BeginSynchronous(callback,state,action,_executeTag); } else { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } // Support Asynchronous behavior. // Execute/ExecuteCore are no longer called. VerifyExecuteCalledOnce(); Initialize(requestContext); return AsyncResultWrapper.Begin(callback,BeginExecuteCore,EndExecuteCore,_executeTag); } }
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。