AbstractApproach:...for a real simple web app with fairly insensitive data it will probably work.
This is, IMNSHO, one of the main problems with a lot of the stuff that MS puts out, and this first version of MVC is no different. I realize this question was more than likely asked from this very perspective (a simple site) but the question is perfectly in line with a major shortcoming of this framework (and others) when you contrast it against the stuff that P&P recommends for enterprise development. MS has stated that SMB developers are their main clientèle, but I would then counter with why is that? I work in an IT department (one of many here) for a large Fortune 500 company, and due to the corporate mentality that we all love so much, I work with mostly junior developers trying to create and maintain large enterprise systems. I am, in effect, trying to train myself to be an architect, because there's just nobody else doing it. I would submit that there is a rather large, unaccounted for, portion of MS's clientèle that are just like me: enterprise developers working on large systems with very little or no architectural oversight or guidance. And we turn to the community at large, and MS in particular, to find some of that guidance. And yet, we find that MS is at odds with itself in some ways. Or putting out stuff that just isn't aimed at us. It can be very frustrating. I know this is just the first drop of MVC, so I'm hoping the "if you're doing enterprise development you'll want to use MVC" statement holds true in the end.
Anyway, now that that's out there... the main point I'm driving at is P&P's guidance concerning composite applications. "Simple" apps will maybe need one little separate admin section, but what about a framework for a large enterprise system that needs many different modules, the ability to plug and unplug modules depending on the customized LOB application built from the framework? Duplicate controllers named Home, indeed!
Here's what I've done to enable module (or area) separation and routing, maybe it will be of value to you in your simple(r) application or to others trying to build a more complex system. I unfortunately had to start from the top, since the routing engine doesn't handle this stuff in any way that I could figure out. So I wrote my own. I'm using Spring.Net for dependency injection, so you can make direct instantiations of the appropriate classes instead to eliminate that complexity if you aren't into DI. I've omitted some details (methods, properties) for brevity (and this is still a lot of code! Sorry).
public class ModuleMvcRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
ModuleMvcHandler handler = new ModuleMvcHandler();
handler.RequestContext = requestContext;
return handler;
}
}
class ModuleMvcHandler : IHttpHandler, IRequiresSessionState
{
void IHttpHandler.ProcessRequest(HttpContext httpContext)
{
this.ProcessRequest(httpContext);
}
/// <summary>
/// Handles the http request
/// </summary>
/// <param name="httpContext"></param>
protected virtual void ProcessRequest(HttpContext httpContext)
{
if (this.RequestContext == null)
{
throw new InvalidOperationException("The MvcHandler requires that a valid RequestContext be set.");
}
string routeModule = GetRequiredString(this.RequestContext.RouteData, "module");
string routeController = GetRequiredString(this.RequestContext.RouteData, "controller");
IController controllerInstance = this.GetControllerInstance(routeModule + "." + routeController);
if (controllerInstance == null)
{
throw new InvalidOperationException(string.Format("The controller for path '{0}' could not be found or it does not implement the IController interface.",
new object[] { this.RequestContext.HttpContext.Request.Path }));
}
ControllerContext controllerContext = new ControllerContext(this.RequestContext, controllerInstance);
controllerInstance.Execute(controllerContext);
}
/// <summary>
/// Creates an instance of the requested controller using the configured IModuleControllerFactory
/// </summary>
/// <param name="controllerName"></param>
/// <returns></returns>
protected virtual IController GetControllerInstance(string controllerName)
{
// instantiate the controller factory
IApplicationContext context = ContextRegistry.GetContext();
IModuleControllerFactory controllerFactory = (IModuleControllerFactory)context.GetObject("IModuleControllerFactory");
// have the factory build the controller
return controllerFactory.CreateController(this.RequestContext, controllerName);
}
}
class ModuleControllerFactory : IModuleControllerFactory {
IController IModuleControllerFactory.CreateController(RequestContext context, string controllerName) {
return this.CreateController(context, controllerName);
}
public virtual IController CreateController(RequestContext context, string controllerName) {
IApplicationContext objectFactory = ContextRegistry.GetContext();
IController controller = (IController)objectFactory.GetObject(controllerName, typeof(IController));
controller = null;
if (controller != null && typeof(Controller).IsAssignableFrom(controller.GetType())) {
// can't do this yet, ViewPage.ViewData is Internal :(
// have to fully qualify view paths in RenderView until can implement custom ViewFactory
//IViewFactory viewFactory = (IViewFactory)objectFactory.GetObject("IModuleViewFactory", typeof(IViewFactory));
}
return controller;
}
}