I'm Keyvan Nayyeri, a 28 years old software engineer working at Match.Com and living in Dallas, Texas.
I have a Master’s degree in computer science and a bachelor's degree in applied mathematics. I’m also known to be a technical author with several technical publications in the form of books and articles. Besides, I'm an open source enthusiast and have coordinated or contributed to several projects. Currently, I maintain my projects on GitHub.
As a content provider on the internet, not only I publish on this technical blog, but also I'm a podcaster and publish audio podcasts on Mash This.
Trying to maintain a healthy and active lifestyle, I'm a pescetarianist and exercise almost everyday. I’m an avid runner, soccer defender, and tennis player. I also have an interest in fashion.
After discussing custom route handler and IRouteHandler as two extensibility points in ASP.NET MVC to customize the behavior of routing system, now I want to continue discussing thirteen major extensibility points in ASP.NET MVC by focusing on custom controller factories and building such controller factories.
When ASP.NET MVC receives a request, it needs to manage how to handle it with a specific controller and the action methods in it. The component that is responsible to map an incoming request to a specific controller and decide which controller to use is controller factory. There is a default controller factory in ASP.NET MVC that maps incoming requests to a controller with a Controller postfix.
The built-in controller factory is the registered controller factory by default and is implemented in DefaultControllerFactory class. Also it’s possible to extend its behavior with minor changes by deriving from this base class.
But in some circumstances you may need to have a fully customized behavior for your controller factories. One common example is when you use Dependency Injection frameworks where you need to use a customized factory. Fortunately, most of the DI frameworks provide such a customized controller factory out of the box, but if you were faced with a case to implement such a factory, you can implement the IControllerFactory interface and register your own custom controller factory.
IControllerFactory is an interface with two methods:
Implementation of a controller factory is comparatively easy, and can be done with less amount of work to be done.
In this post I implement a basic controller factory that loads controllers based on the user’s language, so a specific controller can be loaded for a specific language. In this sample application, I define the type name of controllers in web configuration file based on a pattern that corresponds to a specific culture, and implement a controller factory that loads the appropriate controllers group based on the client’s preferences.
First I define my type patterns in my configuration file as application settings. Here I have two cultures: if the user uses Farsi, then the Farsi controllers will be loaded, otherwise the default English language will be used.
Now I write my controller factory by implementing the IControllerFactory interface and its two methods.
using System;
using System.Configuration;
using System.Web.Mvc;
using System.Web.Routing;
namespace IControllerFactorySample.ControllerFactories
{
public class CustomControllerFactory : IControllerFactory
{
#region IControllerFactory Members
public IController CreateController(RequestContext requestContext, string controllerName)
{
if (string.IsNullOrEmpty(controllerName))
throw new ArgumentNullException("controllerName");
string language = requestContext.HttpContext.Request.Headers["Accept-Language"];
string controllerType = string.Empty;
if (language == "fa-IR")
controllerType = string.Format(ConfigurationManager.AppSettings["FarsiControllerTypePattern"], controllerName);
else
controllerType = string.Format(ConfigurationManager.AppSettings["EnglishControllerTypePattern"], controllerName);
IController controller = Activator.CreateInstance(Type.GetType(controllerType)) as IController;
return controller;
}
public void ReleaseController(IController controller)
{
if (controller is IDisposable)
(controller as IDisposable).Dispose();
else
controller = null;
}
#endregion
}
}
In the CreateController function, I detect the client’s language using the HTTP headers of the request, and load the appropriate type name based on the user’s culture. Then I use reflection APIs to load the type and create an instance of the controller to be returned. Note that this implementation doesn’t mandate the Controller postfix for controller names, so rather than defining my Home controller as HomeController class, I just can use Home name. I have defined my controllers in Fa and En sub-folders inside Controllers folder, so my controller factory can load them based on the type name patterns. Besides, in the ReleaseController method, I dispose the controller as expected.
The third and last step is to add this controller factory as the default factory to ASP.NET MVC. This can be done in Global.asax and its Application_Start method where I use ControllerBuilder.SetControllerFactory to add my factory type as the default controller factory to ASP.NET MVC.
using System.Web.Mvc;
using System.Web.Routing;
using IControllerFactorySample.ControllerFactories;
namespace IControllerFactorySample
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
}
}
}
As you see, building a controller factory is very straightforward and you can get it done in a few simple steps. If I run the application and set my preferred language to Farsi, then Farsi controllers will be used to serve the requests, otherwise English controllers will be loaded.
As always, the sample application for this post is available for download.
Marwan
May 09, 2009 12:09 AM
#
Thanks Keyvan for these simple yet important posts on ASP.NET MVC.
in the line where you check for the language:
if (language == "fa-IR")
if the user set his browser to accept multiple language the Accept-Language header will contain all these language, for example I set my browser to accept the following languages :
English [en-us]
English [en]
Arabic [ar-tn]
French [fr-fr]
the browser send the value "en-us,en;q=0.8,ar-tn;q=0.5,fr-fr;q=0.3" in the Accept-Language header.
so I think it is better to use this code to check for the language:
if (language.StartsWith("en-us"))
Keyvan Nayyeri
May 09, 2009 12:14 AM
#
@Marwan
Thank you for the point.
Yes, it should be better to use language.Contains("fa-IR") to handle all the possibilities.
Khaja Minhajuddin
May 09, 2009 8:50 AM
#
A very good post, It would be great if you had also shown how to test this controller factory. Maybe another post :D
Bart McLeod
May 29, 2009 10:48 AM
#
Thanks for this post, it was very helpful, except for one thing.
Type.GetType(controllerType) return null in my case. I guess it can't find the type, because the controller is in an MVC webapplication and the factory is in an external library, which is references by the webapp. Appreciate any tips.
Bart McLeod
May 29, 2009 11:10 AM
#
Forgive me, posted to soon. I had omitted part of the qualifier of the type (.controllers). It works very well now, thanks.
Cyril Gupta
Jan 01, 2010 9:10 PM
#
Ali
Nov 02, 2010 11:29 AM
#
Majid
Apr 18, 2013 10:33 AM
#
Posted 2009, but still fresh.
damet garm!
Leave a Comment