2014-10-23 4 views
1

Мы недавно обновили нашу базу кода от .Net 4.0 до .Net 4.5.1 и от MVC 2.0 до MVC 5.2.2.Выполнение нескольких действий контроллера при одном вызове с MVC.Net 5

У нас есть собственный метод в нашем базовом классе контроллера, который позволил нам обновить несколько частей наших представлений в рамках одного запроса. Начиная с обновления, это больше не работает.

Оригинальный код:

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[]) 

Для этой функции, как я могу сохранить первоначальное поведение, без ошибок, что у нас было под .Net 4.0 и 2.0 MVC, используя более новую структуру и MVC?

+0

Трудно сказать, не видя остальную часть базы кода, но моя реакция на кишок заключается в том, что вы добавляете слишком много бизнес-логики в свои контроллеры, если вы отправляете людей на несколько действий контроллера. – Paul

+0

@Paul Некоторые из нашего кода довольно сложны и, вероятно, слишком много продолжаются. В этом случае, однако, мы хотим обновить некоторые общие частичные представления, включая их внутреннюю обработку, а также выполнить действие конечного контроллера. Вышеприведенный код является частью страницы соглашения об Условиях обслуживания на нашем сайте и должен возвращать людей к различным действиям контроллера в зависимости от того, с чего они начались. Дополнительные частичные представления всегда должны обновляться при выходе из страницы TOS, но редко в результате конечных действий. – Zarepheth

+2

Этот код дает мне завещания - * особенно * любое упоминание о Server.Transfer в приложении MVC.Похоже, что у вас есть что-то принципиально ошибочное с вашим подходом - вы должны либо нажимать на действие * single * controller, либо на функции обертывания, общие для нескольких страниц в дочерних действиях или частичных, или вы должны использовать AJAX для асинхронного обновления частей представления. Если вам нужно возвращать пользователей в разные места условно, используйте перенаправления и передавайте параметры GET для условного рендеринга. –

ответ

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 и TextWriter текущего объекта Response (this.Response.Output).

Вызов RenderAction на HtmlHelper отобразит мои выбранные результаты действия Controller-Action в поток Response, позволяя нам вызывать несколько действий Controller из других действий контроллера и позволяя нашей платформе AJAX на стороне клиента применять результаты к правильным частям страницы ,

Для мест, где можно выбросить ожидающий поток Response, мы все равно можем использовать Server.TransferRequest. Но теперь это позволяет нам добавлять несколько результатов Controller-Action к одному и тому же потоку ответов с сервера.

Смежные вопросы