Books & ebooks about Microsoft tools, technologies, & research. Plus programming best practices. We hope you enjoy this post.
Dino Esposito’s first book on MVC, Programming Microsoft ASP.NET MVC, is now available for purchase! You can review the book’s TOC and Dino’s fascinating Introduction here. The book’s ISBN is 9780735627147, and its page count is 592 pages. And as you can see from the cover, the book covers ASP.NET MVC 2 and Visual Studio 2010. (Here’s the book at O’Reilly’s site.)
In this post we offer an excerpt from the book. We’ll get you two complete sample chapters in the near future.
Chapter 11
Customizing ASP.NET MVC
We need men who can dream of things that never were. —John F. Kennedy
ASP.NET MVC was built with extensibility in mind and in full respect of many good design principles, such as Dependency Inversion, Open/Closed, and Single Responsibility. As obvious as it might sound, the net effect is just what these principles claim you will get if you apply them systematically. You can extend the application without changing the source code, without painful refactoring, and without heavy regression. If properly designed, your application is then open for extensions but closed for modifications.
Because the Open/Closed principle is mostly a driver for architects and developers, the other two principles provide concrete guidance on how to design classes that favor the injection of custom components to replace built-in functionalities. The simpler and more well-defined a class is, the easier it is for developers to customize and replace built-in functionalities. In this regard, ASP.NET MVC is an excellent example of application design.
Because ASP.NET MVC is ultimately a framework, the benefits of its design will ripple across any applications built on top of it. In this chapter, my goal is to help you discover the points of extensibility you find in ASP.NET MVC and to illustrate them with a few examples. I organized the extensibility points of ASP.NET into three main categories: execution of actions, filters, and view rendering.
Note For more information about the aforementioned design principles, often summarized with the acronym SOLID, you can have a look at a recent book I wrote with Andrea Saltarello, Microsoft .NET: Architecting Applications for the Enterprise (Microsoft Press, 2008). You might find it curious that we don’t use the acronym SOLID anywhere in the text; however, the acronym is a more recent (and nice) invention of some industry gurus. We do, though, cover exactly the principles that contribute their initials to the acronym: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion.
The Controller Factory
I spent a lot of time and effort studying the internal implementation of ASP.NET Web Forms. At the end of the day, any request that hits an ASP.NET Web Forms application is processed by a class that derives from System.Web.UI.Page. This class implements the IHttpHandler interface and does its work through the ProcessRequest method on the IHttpHandler interface. Have you ever run across the implementation of this method?
ProcessRequest is a rather intricate mishmash of different programming styles, and it formsa natural habitat for a number of common code smells: long method calls, endless branches,switch statements, data clumps.
In ASP.NET MVC, any intercepted requests are routed to a new HTTP handler—theMvcHandler class that you met already in Chapter 2, “The Runtime Environment.” This class isdesigned to contain code functionally equivalent to the code in the ProcessRequest methodof the Web Forms’ Page class. The quality of the code in MvcHandler is significantly better—it’s more readable, far easier to maintain and, in particular, extensible.
To understand the extensibility points of ASP.NET MVC, you have to start from controllersand their factory. (And possibly also follow the example they provide in your own code.)
ASP.NET MVC Request Processing
On the way to controllers, the first stop is the MvcHandler class, where each ASP.NETMVC request eventually lands. In Chapter 2, we briefly examined the source code of theMvcHandler class with the purpose of explaining how an MVC request is processed. In thiscontext, we’ll get back to that source code with a different aim: to gain an understanding ofthe mechanics and identify points of extensibility.
For simplicity, I’ll focus on the synchronous way of processing requests that is coded inMvcHandler. For asynchronous calls, the same steps occur, but they are split in two distinctphases—before and after the async point. (See Chapter 4, “Inside Controllers,” for moredetails on asynchronous controllers and asynchronous request processing.)
Inside the MvcHandler Class
The core of an ASP.NET MVC request processing lies in the following code, which is invokeddirectly by the ASP.NET runtime:
protected virtual void ProcessRequest(HttpContext httpContext){ HttpContextBase contextBase = new HttpContextWrapper(httpContext); this.ProcessRequest(contextBase);}
In the first place, the original HTTP context is encapsulated in an HttpContextBase class todecouple the rest of the code from the details of the HTTP runtime environment. These linesof code are the key for mocking and testability, as discussed in Chapter 10, “Testability andUnit Testing.”
The second call to ProcessRequest results in the following behavior:
protected virtual void ProcessRequest(HttpContextBase httpContext){ IController controller; IControllerFactory factory; this.ProcessRequestInit(httpContext, out controller, out factory); try { controller.Execute(this.RequestContext); } finally { factory.ReleaseController(controller); }}
The controller in charge of the request is instantiated and configured in ProcessRequestInit.Then it is given control over the request and released.
The Controller Builder
A first point of extensibility can be found in the ProcessRequestInit method, where theprocess of instantiating the controller is abstracted to a factory. Here are some more details:
private void ProcessRequestInit( HttpContextBase context, out IController controller, out IControllerFactory factory){ this.AddVersionHeader(httpContext); string requiredString = this.RequestContext.RouteData.GetRequiredString("controller"); // Get the factory object for the controller factory = this.ControllerBuilder.GetControllerFactory(); // Create the controller controller = factory.CreateController(this.RequestContext, requiredString); if (controller == null) { throw new InvalidOperationException(); }}
The key thing that is going on in the ProcessRequestInit method occurs in the invocationof the controller builder. ControllerBuilder is a singleton class that holds the defaultinstance of the factory component in charge of creating controller instances:
public ControllerBuilder(){ . . . DefaultControllerFactory controllerFactory = new DefaultControllerFactory(); controllerFactory.ControllerBuilder = this; this.SetControllerFactory(controllerFactory);}
The default factory for controllers is the DefaultControllerFactory class. This class gets thetype of the controller class to instantiate and uses .NET reflection to activate it. In doing so,it assumes a default constructor on the controller class and defaults to that.
The Default Controller Factory
As you can see in the preceding code snippets, the ControllerBuilder class encapsulatesan instance of the controller factory and makes it available through a pair of getter and settermethods. In particular, the SetControllerFactory method is the tool you can use to unplug thedefault controller factory and roll your own.
In Chapter 8, “The ASP.NET MVC Infrastructure,” I demonstrated how to leverage theSetControllerFactory method to introduce an Inversion of Control (IoC)–based controllerfactory that can automatically resolve the chain of dependencies rooted in the controller class.
You register your custom controller factory in Application_Start and have it kick in every timea request is made:
protected void Application_Start(){ . . . // Create and register an IoC-based factory (using the Unity framework) var container = new UnityContainer(); IControllerFactory factory = new MyAppControllerFactory(container); ControllerBuilder.Current.SetControllerFactory(factory);}
At this point, the logic of the controller factory is up to you.
Even though a controller factory is abstracted to a specific interface—the IControllerFactoryinterface—you probably want to start from the DefaultControllerFactory class to create yourown factory. At any rate, the IControllerFactory interface is shown here:
public interface IControllerFactory{ IController CreateController(RequestContext requestContext, string controllerName); void ReleaseController(IController controller);}
The DefaultControllerFactory class implements the interface, but it also exposes overridablemethods at a slightly more granular level than the raw interface.
Extending the Default Controller Factory
In the DefaultControllerFactory class, the CreateController method is a two-step operation:getting the controller’s type and getting an instance of that type. For both of these actions,the DefaultControllerFactory class offers a ready-made virtual method. As a result, there arethree aspects of a controller factory that you might want to customize: getting the type forthe controller in charge of the current request, getting the controller instance, and releasingthe controller instance.
Here’s the list of methods on the DefaultControllerFactory class that you might want to override:
protected virtual IController GetControllerInstance(Type controllerType);protected virtual Type GetControllerType(string controllerName);public virtual void ReleaseController(IController controller);
Let’s examine each scenario in more detail.
Developers, Book excerpts, New books, ASP.NET, MVC