Any URL that hits the ASP.NET MVC runtime environment has to be resolved in terms of a method to be invoked on a controller class. The name of the controller class and the name of the method - also known as the action - to be executed will be figured out in some way. As you may know, there's a default way that is hard-coded in the default Visual Studio template for an ASP.NET MVC application and there are changes you can make to machinery to have controller and action names result from a bit of applied logic. In this article, I'll go through the process of customizing the controller factory to show that the controller name doesn't necessarily result from a token contained in the URL.
Grabbing the Controller Name
When the default URL scheme is used, the controller name is the first token of the URL that follows the server name. Instead, if you opt for a custom URL routing scheme, identifying the controller name is up to you. The controller name may be one of tokens in the URL or it can be a fixed name or it may even be determined algorithmically. Strictly speaking, the controller name is not necessarily the name of the controller class. It is, instead, a moniker that is used to build the actual class name.
From the perspective of the ASP.NET MVC controller machinery the content of the URL may or may not be important for determining the controller name. What really matters is that an appropriate entry is found in the route dictionary packed with the name of the controller. The controller-specific entry in the route dictionary is named "controller". The ASP.NET MVC infrastructure will get it using the following code:
By default, the controller name is read from the URL and the controller class is built by adding a trailing Controller word to the controller name. Therefore, for a controller name of Home, the resulting controller type will be HomeController. As mentioned, this is just the default way of working. If necessary, an alternate approach can be found and implemented. In order of doing so, you need not just a custom route but also a custom route handler.
The following example demonstrates a custom route where the controller token is not specified explicitly, but results from the composition of two other tokens:
The URL scheme is completely custom and doesn't include any token that seems to represent the controller. The route value dictionary includes default values for the tokens in the URL template and not for any controller name. Yet a controller name has to be determined in some way.
The logic that determines the name of the controller class that will ultimately serve a request belongs to the route handler. It goes without saying that if you go for a custom route, you then need a custom route handler that incorporates just this logic. Here's some code for a custom route handler that works with the example:
A route handler is a class that implements the IRouteHandler interface. In the
GetHttpHandler member of the preceding interface, the method first reads any route data that's available and then algorithmically determines the controller name to be used. When done, it adds it to the route data dictionary of the current request context. Finally, the updated request context is passed to the default ASP.NET MVC HTTP handler to serve the request. As an example, if the requested URL is http://server/blogs/home then the resolved controller name is something like
Blogs_Home_ for a resulting controller type of
Blogs_Home_Controller. The trailing Controller suffix is automatically added to the controller name if you stick to the default algorithm for getting the controller type as it is implemented in the DefaultControllerFactory class. By overriding the controller factory and the
GetControllerType method on the factory, you could even change this aspect of the controller build up.
As a side note, consider that just as for the controller name, also the action name can be programmatically configured in case of a custom URL scheme. All you do is figuring out the name of the action by applying any necessary logic and then you store it in the
RouteData collection under the key of "action".
Getting the Controller Type
You override the
GetControllerType method if you want to change the naming convention applied to resolve the controller type. As mentioned, the default convention entails that the controller type name is whatever strings results from appending the word "Controller" to the controller name. You can change the default naming algorithm by creating (and registering) a new controller factory class:
It should be noted that the method returns a Type object that describes the class to instantiate later, not already an instance of the controller type. In addition, the assembly where the type is defined must be available in the current AppDomain. If the system couldn't find a type for the specified controller name then a runtime exception would be thrown.
Getting the Controller Instance
GetControllerInstance method on the controller factory class is responsible for returning a concrete instance of the controller type that was found out by
GetControllerType. The two methods are bound together in the implementation of CreateController in the default controller factory class.
You override the
GetControllerInstance method when you need to change something in the way in which a controller is instantiated. Here's the implementation of the method in the DefaultControllerFactory class:
A common reason for replacing the controller factory and the way in which a instance is created is to enable dependency injection and enabling scenarios where the controller class receives a reference to the service layer class, the data access repository, or whatever other cross-cutting dependencies it may need. If for whatever reason the controller type is not configurable through the IoC, then the following code will still work:
The factory is also the place where you can add the code that configures any instance of a controller. This typically happens when you have a base controller class with custom properties. Another scenario is when you need to customize the action invoker.
Releasing the Controller Instance
The controller factory also exposes a method that takes care of the disposal of the controller instance. Most of the time, you don't need code significantly different from what the default implementation of the method in DefaultControllerFactory offers.
However, if your controller instantiates its own resources, that is a good place to get rid of them. When you employ an IoC container to create the controller instance you might also want to tear the instance down from the container. Here's the you need if you use Unity as your IoC.
When you use an IoC container to instantiate a controller you probably don't need more than a transient instance created on purpose for the request and disposed of at the end of the request lifecycle. However, a controller class may have injected via IoC a number of external objects some of which may have been configured with a different lifetime (i.e., singleton). In this case, disposing of them in ReleaseController may be too early and cause you troubles.
The creation of the controller is an important moment in the lifecycle of an ASP.NET MVC request. An ad hoc class - the controller factory - is responsible for getting you the controller instance to work with. You can define a constructor in your controller class to define some initialization steps or you can replace the default factory class. In doing so, you can take control of nearly any other aspects of the controller cycle including the identification of the type and disposal of the instance.
Dino Esposito is one of the world's authorities on Web technology and software architecture. Dino published an array of books, most of which are considered state-of-the-art in their respective areas. His most recent books are “Microsoft ® .NET: Architecting Applications for the Enterprise” and “...
This author has published 54 articles on DotNetSlackers. View other articles or the complete profile here.
Please login to rate or to leave a comment.