Search code examples
asp.net-mvcmodel-view-controllereditorforeditorfortemplate

MVC EditorFor templates not used when calling controller from another controller


Within an MVC project we have an area with a controller SomeController returning a partial view containing EditorFor statements, each with their own template.

Everything works fine if the controller is invoked directly via a route for that area. However, if it's called via another controller outside of the area, i.e. via 'new SomeController().SomeAction()', the templates are not used, even if explicitly specified (the view is returned ok, but shows just default textboxes etc).

What could be the reason for this and how can it be fixed?


Solution

  • When your action is invoked merely using ctrl.Action(), the current RouteData will be used (with the current area/controller/action values in it) and when Razor tries to resolve your EditorTemplates paths it consults the ViewContext that is still containing the (now wrong) values of the originating action.

    You better use the ControllerFactory in order to mimic the desired behavior:

    var ctrlFactory = ControllerBuilder.Current.GetControllerFactory();
    
    var routeData = new RouteData();
    routeData.DataTokens.Add("area", "target_area_name");
    routeData.Values.Add("controller", "target_controller_name");
    routeData.Values.Add("action", "target_action_name");
    
    var requestContext = new RequestContext(this.HttpContext, routeData);
    var ctrl = ctrlFactory.CreateController(requestContext, "target_controller_name") as TargetControllerType;
    
    if (ctrl != null)
    {
        ctrl.ControllerContext = new ControllerContext(requestContext, ctrl);
    
        var ctrlDesc = new ReflectedControllerDescriptor(typeof(TargetControllerType));
        var actionDesc = ctrlDesc.FindAction(ctrl.ControllerContext, "target_action_name");
    
        var result = actionDesc.Execute(ctrl.ControllerContext, new Dictionary<string, object>()) as ActionResult;
    
        this.RouteData.DataTokens["area"] = "target_area_name";
        this.RouteData.Values["controller"] = "target_controller_name";
        this.RouteData.Values["action"] = "target_action_name";
    
        return result;
    }
    

    See MSDN