I was able to finally hack up a conversion of the core of my old content management system into the new MVC framework. All the issues you guys discussed here were real problems for me. The biggest issues I'd like to see some framework help from was the execution of Controller methods and Rendering of partial views.
I like your solution shinakuma, in that you call ProcessRequest. That seems a bit heavy though, to just call a controller method and output a snippet of html. I managed to use IController's Execute method, but I think that's too heavy as well, plus it was very hackish to get it to work.
In my cms, you can either navigate to a particular page, or render a module directly. The former is old school portal stuff where the engine determines all the views to throw on the page and renders them in the right places. The latter is just like a normal MVC route, with the exception that the rendered html then goes in the main content area of the site template. The cms engine then figures out the home page for that module (basically all the views in a controller share a home page), then draws all the modules/portlets/webparts/partialviews in all the areas outside the main content area. Any POSTs will be handled via AJAX or will be interactions solely with the module represented in the main content area, so all the Route stuff should work just fine. Here's the basic workflow relevant to the discussion you guys have been having:
-
Created custom Controller class that overrides RenderView
-
RenderView checks a context variable "isPageBuildingInProgress"
-
If not, take the view being rendered (remember, a controller action has already been executed) and capture it in a string - to be placed in the main content area
-
If page building is in progress, just render to a string and store it in the right place (number 6 below)
-
Interact with the model to get all the partial views that belong in all the other areas of the page (a controller and an action will come back for each)
-
Execute the appropriate actions on each of the controllers, capturing the rendered html and placing it in StringBuilders on a PageData object (ViewData for the page)
-
Render "THE" aspx view (there is only one in the whole app, everything else is a ViewUserControl) - it binds to the PageData object which just contains html for each of the content areas within the MasterPage
The benefit of this implementation is that all my controllers just call RenderView and know nothing about the cms view manipulation that is going on. The exact same code is executed whether the partial view is the main attraction (directly accessed in the url to show up in the main content area) or if it's just been selected to show up in a particular location by the user for that particular page (rendered due to configuration of the portal). All of the logic is in the base Controller class's Render method, leaving all the controllers and their views for all the modules very clean and nicely encapsulated.
The drawback is the hackish nature of the code I had to mangle to get this to work. Messing with the ControllerContext and RouteData made me feel particularly dirty. Here are some code snippets (Disclaimer - I'm certain there are better ways to do this, but this is as far as I got):
protected void Execute(System.Web.IHttpContext httpContextInterface, RouteData routeData, string controllerName, string action) {
routeData.Values.Clear();
routeData.Values.Add("controller", controllerName);
routeData.Values.Add("action", action);
routeData.Values.Add("id", null);
ControllerContext controllerContext = new ControllerContext(new RequestContext(httpContextInterface, routeData), (IController)this);
base.Execute(controllerContext);
}
When executing the final Render for the page (all partial views rendered with their html strings securely fastened to my PageData object), I had to do the following:
// Now, render the containing page, passing in the pageData object, which contains all the html for each of the SiteTemplate locations
RouteData routeData = this.RouteData;
routeData.Values.Clear();
routeData.Values.Add("controller", "Nav");
routeData.Values.Add("action", "Show");
routeData.Values.Add("id", null);
ControllerContext controllerContext = new ControllerContext(new RequestContext(this.HttpContext, routeData), (IController)this);
this.ControllerContext = controllerContext;
base.RenderView("~/Views/Nav/Show.aspx", masterName, pageData);
Oh, and I used home grown reflection code to create all my partial view controllers to call Execute on. I think I've got some more work ahead of me to handle all the data that can be passed into the url route in my Execute method, but calling ProcessRequest seems like a bit much to accomplish a partial view render.