Introduction
In a simple statement if I want to define an ASP.NET MVC controller then
I can say that it has classes which are responsible for receiving and processing incoming requests, handling client input,
and sending response back to the client. Controllers also act as a coordinator between Model (Business) and View (Presentation).
ASP.NET MVC framework itself create controller object at run time. Only prerequisite is controller class must have a parameter less constructor.
But if you need to pass some objects with constructor then what will happen? Simply framework failed to create controller object. In that scenario we need
to create controller object by our self and injection dependency there.
There are many ways to inject dependency to a class. For example it might be Property setter injection, Method injection, Constructor injection.
In this article I will explain how to inject controller dependency to ASP.NET MVC framework with constructor. Without built custom controller
factory, dependency injection to controller is not possible, so I also show how to create a very simple custom controller factory and register it to
ASP.NET MVC framework.
Why controller dependency injection is needed
In real life application development, you will see almost all
ASP.NET MVC applications need to inject dependency component. You can create component directly inside controller instead of inject that dependency. In that case the controller will be dependent (strongly coupled) on that component. If any component implementation changed or new version of that component released then you need to change controller class itself. Another problem will face when you go for unit testing. You cannot unit test of those controllers independently (within isolation). You cannot take mocking feature from unit testing framework. Without mocking, you cannot unit test your code in isolation.
Controller static structure
ASP.NET MVC framework’s controller structure is defined inside abstract controller class. If want to create any controller, create a class which will be inherited from
Controller
abstract class. The UML class diagram of controller class and its hierarchy looks
Controller root is
IController
interface. Its abstract implementation is
ControllerBase
class. Another controller abstract class is inherited from
ControllerBase
class. All our custom controller classes should be inherited from
Controller
abstract class or its child class.
Simple custom controller definition
If you create an ASP.NET MVC project, you will get two default controllers:
AccountController
and HomeController
.
If you look at the HomeController
class definition, the class has no constructor.
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
}
We all know that if there is no constructor defined then at compile time .NET
Framework creates a default parameter-less constructor like as follows
public class HomeController : Controller
{
public HomeController()
{
}
}
Now create ILogger
interface and its implementation
DefaultLogger
class for using Home controller class. I want to inject it throw constructor.
public interface ILogger
{
void Log(string logData);
}
public class DefaultLogger : ILogger
{
public void Log(string logData)
{
System.Diagnostics.Debug.WriteLine(logData, "default");
}
}
Now HomeController
with ILogger
constructor injection looks like
public class HomeController : Controller
{
private ILogger _logger;
public HomeController(ILogger logger)
{
_logger = logger;
}
}
Still I do not find any place where I can create DefaultLogger
object in my codebase and though I am not creating
HomeController
object by myself so I do not find the place where I can create
DefaultLogger
object and how I can pass it to the HomeController
with defined parameterized constructor
HomeController
(ILogger logger
). In that state if I build my project it will build without any problem. But in runtime it will throw exception. Exception details in error page like as follow
See stack trace above,
DefaultContollerActivator
throw MissingMethodException
. If you go
MSDN, search when the exception is raised, will find there and it clearly mentions “The exception that is thrown when there is an attempt to dynamically access a method that does not exist.” That is the inner exception message. If see next exception
InvalidOperationException
, it is actually wrapped MissingMethodException
and there more user friendly message generated that is “Make sure that the controller has a parameterless public constructor.” If make workable to Home controller then it must have a parameterless
constructor and framework will create controller object with the help of that constructor. Question will rise how I can pass
DefaultLogger
object to that controller? Please keep your patience.
How framework create controllers
Before start to dependency injection process of DefaultLogger
object to controller, we should have a clear picture how MVC framework create controller object.
IControllerFactory
interface is responsible for creating controller object.
DefaultControllerFactory
is its default framework provided implementation. If you add a parameter less constructor to the
HomeController
class and set break point there and run application with
debug mode, you can see that IControllerFactory
contain
DefaultControllerFactory
object. DefaultControllerFactory
has various methods
like Create
,
GetControllerInstance
, CreateController
are playing role for creating
HomeController
object. You know that ASP.NET MVC framework is open source project, so if you want to know more details about those methods then you can download its source code and see the implementation. ASP.NET
MVC implement abstract factory design pattern for creating controller class. If you debug code and see value with quick watch then you can see that
DefaultControllerFactory
is automatically created as CurrentControllerFactory
(IControllerFactory
).<o:p>
Why need to create custom controller factory
Already you know that Default controller factory create controller object using parameter less constructor from controller class. But we need parameterize constructor so that we can inject our component dependency though that constructor. So
DefaultControllerFactory
current implementation does not support our requirement. So we need a new controller factory class.
Create custom controller factory
We can create a new controller factory by implementing
IControllerFactory
interface. Suppose our new controller factory named
CustomControllerFactory
. So its implementation looks like
public class CustomontrollerFactory : IControllerFactory
{
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
ILogger logger = new DefaultLogger();
var controller = new HomeController(logger);
return controller;
}
public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(
System.Web.Routing.RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
disposable.Dispose();
}
}
You need to register CustomControllerFactory
to MVC framework. We can do it inside
Application_Start
event.
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
RegisterCustomControllerFactory ();
}
}
private void RegisterCustomControllerFactory ()
{
IControllerFactory factory = new CustomControllerFactory();
ControllerBuilder.Current.SetControllerFactory(factory);
}
If you run application and see that your parameterless HomeController
constructor is not called, instead parameterized
HomeController(ILogger logger)
is called by framework.
You can create your controller creation with little generic way using reflection.
public class CustomControllerFactory : IControllerFactory
{
private readonly string _controllerNamespace;
public CustomControllerFactory(string controllerNamespace)
{
_controllerNamespace = controllerNamespace;
}
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
ILogger logger = new DefaultLogger();
Type controllerType = Type.GetType(string.Concat(_controllerNamespace, ".", controllerName, "Controller"));
IController controller = Activator.CreateInstance(controllerType, new[] { logger }) as Controller;
return controller;
}
}
First you need to create controller type object from controller full name. Then you create controller object at run time using
Type.GetType
method and inject dependency object to that object through reflection. In current code implementation has some problems which are: i) You need to pass controller namespace (using
constructor) so every controller should be same namespace and ii) all controllers need single parameterized construction which accept
ILogger
object only. Without that it will throw
MissingMethodException
.
Another approach
<o:p>
Another way you can create your own controller factory. Though MVC framework’s default implementation of
IControllerFactory
is DefaultControllerFactory
and it is not
a sealed
class. So you can extend that class and override whatever you need to changed. One implementation like
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
ILogger logger = new DefaultLogger();
IController controller = Activator.CreateInstance(controllerType, new[] { logger }) as Controller;
return controller;
}
}
just created my own CustomControllerFactory
class inherited from
DefaultControllerFactory
and override GetControllerInstance
method.
Take MEF's help for creating custom controller factory
In real life project you will see experts are using IOC containers inside controller factory for creating/fetching
the Controller
object. Why because many problem need to raise and handle if you try to dynamically create dependent object and controller class. So it is a better approach to use IOC container to our project and register all my dependent objects there and use it. You can use various IOC containers like Castle Windsor, Unity, NInject there. I will demonstrate how MEF is used there. By the way MEF comes with .NET framework 4.0 so no third party component dependency there. First you take to reference of
system.ComponentModel.Composition
to your project.
Then need to create custom Controller Factory. The name of the controller factory is MefControllerFactory
.
public class MefControllerFactory : DefaultControllerFactory
{
private readonly CompositionContainer _container;
public MefControllerFactory(CompositionContainer container)
{
_container = container;
}
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
Lazy<object, object> export = _container.GetExports(controllerType, null, null).FirstOrDefault();
return null == export
? base.GetControllerInstance(requestContext, controllerType)
: (IController)export.Value;
}
public override void ReleaseController(IController controller)
{
((IDisposable)controller).Dispose();
}
}
CompositContainer
object is works like as
IOC container here. Inside GetControllerInstance
method I fetch controller object from that. If found null value then I fetch it from default controller (base class) object otherwise from
CompositContainer
object. After creating MefControllerFactory
class we need to register it to MVC framework. Registration code in Application Start event<o:p>
protected void Application_Start()
{
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var composition = new CompositionContainer(catalog);
IControllerFactory mefControllerFactory = new MefControllerFactory(composition);
ControllerBuilder.Current.SetControllerFactory(mefControllerFactory);
}
I use InheritedExportAttribute
, ExportAttribute
, PartCreationPolicyAttribute
of MEF to interface
ILogger
, HomeController
. Based on these attributes MEF framework create object. Just one important think you should remember that when use MEF you should decorate your controller through PartCreationPolicyAttribute
to your controllers and set controllers life time as create object per request policy. [PartCreationPolicy (CreationPolicy.NonShared)]
Without that you
will get an error.
Points of Interest
I tried to explain various ways to create controller factory and inject its dependency in a very simple and state forward way. First 1 or 2 approach is just making understandable to the process of building and injecting controller and its dependency. You should not use that approach directly to the real life application. You can more reach on MEF and choose MEF approach to use that to your real life projects. Near future I have a plan to write another article that will demonstrate to use various IOC containers like Windsor, Unity, NInject etc. to inject controller dependency injection and a comparative study with them. In my sample downloadable code I used visual studio 2012 with .NET framework 4.5. Anyone can download and play with that.<o:p>