经过几个小时的浏览ASP.NET MVC源代码,我能提供的最佳解决方案(除了创建每个控制器操作的同步版本)是在
Controller.HandleUnknownAction
中手动调用异步Action方法的操作描述符。虽然我对这段代码不是特别满意,但它确实可以工作。思路是故意请求一个无效的操作(以“_”为前缀),这将在控制器上调用
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 })
我几乎可以确定有一种更好的方法,通过覆盖 Controller.BeginExecute
。默认实现如下所示。想法是立即执行 Controller.EndExecuteCore
,尽管到目前为止我还没有成功。
protected virtual IAsyncResult BeginExecute(RequestContext requestContext, AsyncCallback callback, object state)
{
if (DisableAsyncSupport)
{
Action action = () =>
{
Execute(requestContext);
};
return AsyncResultWrapper.BeginSynchronous(callback, state, action, _executeTag);
}
else
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
VerifyExecuteCalledOnce();
Initialize(requestContext);
return AsyncResultWrapper.Begin(callback, state, BeginExecuteCore, EndExecuteCore, _executeTag);
}
}
Html.ActionAsync
,它将简单地调用管道以满足您的需求,但在助手方法体的末尾添加.Result
调用,从而将异步调用转换为同步调用。这确实是一个有趣的问题。 - TejsHttpServerUtilityBase.Execute
的深处。@ThikingSites - 为什么不发表一个更详细的答案呢?这听起来像一个很好的解决方法。 - Ben Foster