加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc – 在ASP.NET MVC 4中强制同步执行异步操作

发布时间:2020-12-16 03:38:56 所属栏目:asp.Net 来源:网络整理
导读:由于MVC 4不支持异步子操作(通过 Html.Action),我正在寻找一种强制同步执行子操作的方法.解决此限制的一个简单方法是提供所有控制器操作的同步版本: public class FooAsyncController : Controller { public async TaskActionResult IndexAsync() { var mod
由于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);
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读