ASP.NET MVC ソース コードを何時間も調べた結果、(すべてのコントローラー アクションの同期バージョンを作成する以外に) 思いついた最善の解決策は、.NET 内の非同期アクション メソッドのアクション記述子を手動で呼び出すことController.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, state, BeginExecuteCore, EndExecuteCore, _executeTag);
}
}