Performing multiple controller actions in a single call using MVC.Net 5

We recently upgraded our code base from .Net 4.0 to .Net 4.5.1 and from MVC 2.0 to MVC 5.2.2.

We have our own method in our base controller class, which allows us to update several parts of our views within a single request. Starting with the update, this no longer works.

Original code:

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); } 

We get errors when calling httpHandler.ProcessRequest . We have used this technique in many places. After long searches, it seemed like Server.TransferRequest should be used Server.TransferRequest .

New code

 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); } 

When called from this code:

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

Our new code generates this error:

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

Since the message clearly says that I cannot call TransferRequest more than once, but my code must perform two controller actions in addition to redirecting and performing the third action, I thought I would return to the old code. However, this generates this error:

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

For this function, how can I keep the original behavior without the errors that we had in .Net 4.0 and MVC 2.0 when using the newer structure and MVC?

+1
source share
1 answer

After many studies, I came up with the following:

 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 is built with the new ViewContext. The new ViewContext is constructed with most of the data from the current ControllerContext and the current Response TextWriter ( this.Response.Output ).

Calling RenderAction on the HtmlHelper will map my selected Controller-Action results to the Response stream, allowing us to call several Controller actions from other Controller actions and letting our client-side AJAX platform apply the results to the right parts of page

For places where you can throw the pending Response stream, we can still use Server.TransferRequest . But now it allows us to add multiple Controller-Action results to the same response stream from the server.

0
source

All Articles