【问题标题】:Async Controller Action with Umbraco 7 returns stringUmbraco 7 的异步控制器操作返回字符串
【发布时间】:2014-05-25 06:39:45
【问题描述】:

是否可以在 Umbraco SurfaceController(和 UmbracoApiController)中使用异步操作

我尝试了以下代码

public async Task< ActionResult> HandleLogin(LoginViewModel model)
{
    await Task.Delay(1000);
    return PartialView("Login", model);
}

虽然它在调用动作时正确编译,但动作似乎在等待被命中后立即返回,并返回一个字符串

System.Threading.Tasks.Task`1[System.Web.Mvc.ActionResult]

控制器当然继承自SurfaceController,我想知道这是否是问题所在?

如果这不可能,是否有任何解决方法来实现异步操作行为?

任何帮助将不胜感激!

【问题讨论】:

  • 我对 Umbraco 不熟悉,但是它将 Task&lt;ActionResult&gt; 转换为字符串这一事实表明它不理解 async 方法。您可能需要直接联系 Umbraco 社区,和/或提出功能请求。
  • 谢谢,是的,我认为它可能是这样的。也在 our.umbraco 上问过一个问题。如果有任何反馈,会在这里更新!
  • 我使用 RenderMvcController 在stackoverflow.com/questions/30166566/… 上发布了一个类似的问题 - 运气好吗?一年过去了,但仍然是同样的问题!我错过了什么吗?
  • 对于继承自 Umbraco.Web.Mvc.SurfaceController 的控制器,此问题已得到修复,但对于实现 IRenderMvcController 的控制器来说,这仍然是一个问题。我已经将它作为一个问题提出here 并写了关于a workaround here

标签: c# asp.net-mvc asp.net-mvc-4 umbraco async-await


【解决方案1】:

Umbraco 中的 SurfaceControllers 最终派生自 System.Web.Mvc.Controller 但是它们具有自定义操作调用程序 (RenderActionInvoker) 集。

RenderActionInvoker 继承自 ContollerActionInvoker。为了处理异步操作,它应该从 AsyncContolkerActionInvoker 派生。 RenderActionInvoker 仅覆盖 findaction 方法,因此更改为从 AsyncContolkerActionInvoker 派生很容易。

一旦我使用此更改重新编译 Umbraco.Web,异步操作就可以正常工作。

我猜你可以在每个类上指定一个新的 actioninvoker,而不是重新编译整个项目

public class RenderActionInvokerAsync : System.Web.Mvc.Async.AsyncControllerActionInvoker
{

    protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
    {
        var ad = base.FindAction(controllerContext, controllerDescriptor, actionName);

        if (ad == null)
        {
            //check if the controller is an instance of IRenderMvcController
            if (controllerContext.Controller is IRenderMvcController)
            {
                return new ReflectedActionDescriptor(
                    controllerContext.Controller.GetType().GetMethods()
                        .First(x => x.Name == "Index" &&
                                    x.GetCustomAttributes(typeof(NonActionAttribute), false).Any() == false),
                    "Index",
                    controllerDescriptor);

            }
        }
        return ad;
    }

}

public class TestController : SurfaceController
{

    public TestController() {
        this.ActionInvoker = new RenderActionInvokerAsync();
    }

    public async Task<ActionResult> Test()
    {
        await Task.Delay(10000);
        return PartialView("TestPartial");

    }
}

虽然没有测试过这种做事方式。

【讨论】:

  • 谢谢皮特。您的回答帮助我解决了我自己的问题(尽管不是 Umbraco)。在 MVC5 中,我的 ControllerFactory 连接了一个旧的 ActionInvoker 以连接到继承自 ControllerActionInvoker 的 ELMAH。当我将其更改为 AsyncControllerActionInvoker 时,问题就解决了。
【解决方案2】:

仅供参考,我为此向跟踪器添加了一个问题: http://issues.umbraco.org/issue/U4-5208

有一个解决方法:

创建自定义异步渲染操作调用(如上所述):

public class FixedAsyncRenderActionInvoker : System.Web.Mvc.Async.AsyncControllerActionInvoker
{
    protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
    {
        var ad = base.FindAction(controllerContext, controllerDescriptor, actionName);

        if (ad == null)
        {
            //check if the controller is an instance of IRenderMvcController
            if (controllerContext.Controller is IRenderMvcController)
            {
                return new ReflectedActionDescriptor(
                    controllerContext.Controller.GetType().GetMethods()
                        .First(x => x.Name == "Index" &&
                                    x.GetCustomAttributes(typeof(NonActionAttribute), false).Any() == false),
                    "Index",
                    controllerDescriptor);

            }
        }
        return ad;
    }

}

创建自定义渲染 mvc 控制器:

public class FixedAsyncRenderMvcController : RenderMvcController
{
    public FixedAsyncRenderMvcController()
    {
        this.ActionInvoker = new FixedAsyncRenderActionInvoker();
    }
}

创建自定义渲染控制器工厂:

public class FixedAsyncRenderControllerFactory : RenderControllerFactory
{
    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controller1 = base.CreateController(requestContext, controllerName);
        var controller2 = controller1 as Controller;
        if (controller2 != null)
            controller2.ActionInvoker = new FixedAsyncRenderActionInvoker();
        return controller1;
    }
}

创建一个 umbraco 启动处理程序并将必要的部分替换为上述自定义部分:

public class UmbracoStartupHandler : ApplicationEventHandler
{
    protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        DefaultRenderMvcControllerResolver.Current.SetDefaultControllerType(typeof(FixedAsyncRenderMvcController));

        FilteredControllerFactoriesResolver.Current.RemoveType<RenderControllerFactory>();
        FilteredControllerFactoriesResolver.Current.AddType<FixedAsyncRenderControllerFactory>();

        base.ApplicationStarting(umbracoApplication, applicationContext);
    }
}

【讨论】:

    猜你喜欢
    • 2018-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    • 2016-02-27
    • 2013-05-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多