1

我们最近将代码库从 .Net 4.0 升级到了 .Net 4.5.1,从 MVC 2.0 升级到了 MVC 5.2.2。

我们的基本控制器类中有一个自定义方法,它允许我们在单个请求中更新视图的多个部分。自升级以来,这不再有效。

原始代码:

protected void IncludeAction(string actionName, string controllerName, object routeValues)
{
    //Get Url
    RouteValueDictionary routes = null;
    if (routeValues != null)
        routes = new RouteValueDictionary(routeValues);
    else
        routes = new RouteValueDictionary();
    routes.Add("Action", actionName);
    if (!string.IsNullOrEmpty(controllerName))
        routes.Add("Controller", controllerName);
    else
        routes.Add("Controller", this.ControllerContext.RouteData.Values["Controller"].ToString());
    var url = RouteTable.Routes.GetVirtualPath(this.ControllerContext.RequestContext, routes).VirtualPath;

    //Rewrite path
    System.Web.HttpContext.Current.RewritePath(url, false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(System.Web.HttpContext.Current);
}

httpHandler.ProcessRequest我们在通话中收到错误。我们在很多地方都使用了这种技术。经过大量谷歌搜索后,我们似乎应该使用它Server.TransferRequest

新代码

protected void IncludeAction(string actionName, string controllerName, object routeValues)
{
    //Get Url
    RouteValueDictionary routes = null;
    if (routeValues != null)
        routes = new RouteValueDictionary(routeValues);
    else
        routes = new RouteValueDictionary();
    routes.Add("Action", actionName);
    if (!string.IsNullOrEmpty(controllerName))
        routes.Add("Controller", controllerName);
    else
        routes.Add("Controller", this.ControllerContext.RouteData.Values["Controller"].ToString());
    var url = RouteTable.Routes.GetVirtualPath(this.ControllerContext.RequestContext, routes).VirtualPath;

    //Rewrite path
    System.Web.HttpContext.Current.RewritePath(url, false);
    System.Web.HttpContext.Current.Server.TransferRequest(url, true);
}

从这样的代码调用时:

IncludeAction("OptInBanner", "Person");
IncludeAction("NavMenu", "Person");
return Transfer(returnurl);

我们的新代码会产生这个错误:

Type:
    System.InvalidOperationException
Message:
    TransferRequest cannot be invoked more than once.
Stack Trace:
    at System.Web.HttpServerUtility.TransferRequest(String path, Boolean preserveForm)
    at MyProject.MyNamspace.MyBaseController.IncludeAction(String actionName, String controllerName, Object routeValues)
    at MyProject.MyNamspace.MyBaseController.IncludeAction(String actionName, String controllerName)
    at MyProject.MyNamspace.MyController.MyAction(Boolean myChoice, String returnurl)
    at .lambda_method(Closure , ControllerBase , Object[] )

由于消息明确表示我不能多次调用 TransferRequest,但我的代码除了重定向和执行第三个操作之外还需要执行两个控制器操作,我想我会恢复到旧代码。但是,这会产生此错误:

Type:
    System.InvalidOperationException
Message:
    'HttpContext.SetSessionStateBehavior' can only be invoked before 'HttpApplication.AcquireRequestState' event is raised.
Stack Trace:
    at System.Web.Routing.UrlRoutingHandler.ProcessRequest(HttpContextBase httpContext)
    at MyProject.MyNamspace.MyBaseController.IncludeAction(String actionName, String controllerName, Object routeValues)
    at MyProject.MyNamspace.MyBaseController.IncludeAction(String actionName, String controllerName)
    at MyProject.MyNamspace.MyController.MyAction(Boolean myChoice, String returnurl)
    at .lambda_method(Closure , ControllerBase , Object[] )

对于此功能,我如何在使用较新的框架和 MVC 时保留我们在 .Net 4.0 和 MVC 2.0 下的原始行为,而不会出现错误?

4

1 回答 1

0

经过大量研究,我想出了这个:

protected void IncludeAction(string actionName, string controllerName, object routeValues)
{
    string targetController = null;
    if (!string.IsNullOrWhiteSpace(controllerName))
    {
        targetController = controllerName;
    }
    else
    {
        targetController = this.ControllerContext.RouteData.Values["Controller"].ToString();
    }

    HtmlHelper htmlHelper = new HtmlHelper(
        new ViewContext(
            this.ControllerContext,
            new WebFormView(this.ControllerContext, actionName),
            this.ViewData,
            this.TempData,
            this.Response.Output
        ),
        new ViewPage()
    );

    htmlHelper.RenderAction(actionName, targetController, routeValues);
}

HtmlHelper 是用一个新的 ViewContext 构造的。新的 ViewContext 由当前 ControllerContext 和当前 Response 对象的 TextWriter ( this.Response.Output) 中的大部分数据构成。

调用RenderActionHtmlHelper 会将我选择的 Controller-Action 结果呈现给 Response 流,允许我们从其他 Controller Actions 调用多个 Controller Actions,并让我们的客户端 AJAX 框架将结果应用到页面的正确部分。

对于可以丢弃待处理的响应流的地方,我们仍然可以使用Server.TransferRequest. 但这现在允许我们将多个 Controller-Action 结果附加到来自服务器的同一响应流。

于 2014-11-04T16:20:18.797 回答