Click here to Skip to main content
15,860,972 members
Articles / Web Development / ASP.NET

ASP.NET MVC Controller Dependency Injection for Beginners

Rate me:
Please Sign up or sign in to vote.
4.82/5 (129 votes)
31 Dec 2013CPOL9 min read 623.4K   4.7K   216   75
In this article I demonastrate a very simple and straightforward way to inject controller dependency to ASP.NET MVC framework with constructor.

Table of Contents

1. Introduction    

 Image 1

Simply if I want to define an ASP.NET MVC controller responsibility then I can say  following are the main responsibilities: 

  • Receiving HTTP request  
  • Processing incoming HTTP requests 
  • Handling client input 
  • Sending response back    
  • Coordinating between Model & View 

Image 2 

ASP.NET MVC framework itself creates controller objects at run time. There is only one prerequisite, that is controller class must have a parameter less constructor. But if you need to pass objects as constructor parameters and for that reason if you create parameterized constructor then what will happen? Simply framework will fail to create those controllers object that have parameterized constructor. In that case we need to create controller objects by our self and inject controller parameters there. 

There are many ways you can inject dependency to a class. For example:

  • Setter Property
  • Method
  • Constructor  

In this article I explain with code sample how to inject controller dependency to ASP.NET MVC framework using constructor. Without creating custom controller factory, inject dependency to controllers are not possible. So I also explain how to create a very simple custom controller factory and register it to ASP.NET MVC framework. I also demonstrate a way to inject dependency to controllers using Managed Extensible Framework (MEF).  

2. Why Inject Controller Dependency? 

In real life application development, you will see almost all ASP.NET MVC applications are needed to inject its dependent component. You can create component directly inside the controller instead of inject them. In that case the controller will be strongly coupled on those components. If any component's implementation is changed or new version of that component is released then you must change controller class itself.

Another problem you will face when you will write unit test. You cannot run unit tests of those controllers independently (within isolation). You cannot take mocking features from unit testing framework. Without mocking, you cannot run unit test of your code in a isolated environment.  

3. Controller Static Structure  

ASP.NET MVC framework’s controller structure is defined inside an abstract class named Controller. If you want to create any controller, first you will create a class which must be inherited from abstract class Controller.  The UML class diagram of controller class and its hierarchy looks: 

1

All controllers root interface is IController interface. Its abstract implementation is ControllerBase class. Another abstract class is inherited from ControllerBase class and name of that class is Controller. All our custom controller classes should be inherited from that Controller abstract class or its any child class. 

4. Simple Custom Controller   

If you create an ASP.NET MVC project, you will get two default controllers. One is AccountController and another is HomeController

2

Image 5

If you look at the HomeController class definition, then you will find that the class has no constructor. 

C#
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. 

C#
public class HomeController : Controller
{
    public HomeController()
    {
    }
} 

Now I will create ILogger interface and its implementation DefaultLogger class. Home controller class will use that ILogger object. I will inject it throw constructor.   

C#
public interface ILogger
{
    void Log(string logData);
}
public class DefaultLogger : ILogger
{
    public void Log(string logData)
    {
        System.Diagnostics.Debug.WriteLine(logData, "default");
    }
} 

HomeController with ILogger constructor injection looks: 

C#
public class HomeController : Controller
{
    private readonly 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 HomeController (ILogger logger) constructor. In that state if I build my project, it will build without any errors. But in run time it will throw exception. Exception details in error page looks: 

4

See the stack trace above, object of class DefaultContollerActivator throw exception named MissingMethodException. If you go MSDN, search the exception which is raised, you will find it there and there 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 named InvalidOperationException, it is actually wrapped MissingMethodException and there you will find  more user friendly message and that is “Make sure that the controller has a parameterless public constructor.” If I want to make  HomeController workable then I must add a parameter less 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.    

5. How MVC Framework Creates Controller? 

Before start to describe dependency injection process of DefaultLogger object to the HomeController, we should have one clear picture and that is how to create controller object by MVC framework? IControllerFactory interface is responsible for creating controller object. DefaultControllerFactory is its default framework provided implementation class. If you add a parameter less constructor to the HomeController class and set break point in that and run application with debug mode, you will find that the code execution process is hold on to that breakpoint. 

Image 7

If you look at the bellow image, you can see that IControllerFactory type variable named factory contains DefaultControllerFactory object. DefaultControllerFactory has various methods like Create, GetControllerInstance, CreateController those are playing vital 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 and their behavior then you can download its source code and see the implementation. ASP.NET MVC implements abstract factory design pattern for creating controller class. If you debug code and see the value with quick watch then you can see that DefaultControllerFactory is automatically created as CurrentControllerFactory (IControllerFactory).<o:p> 

Image 8

6. Why Custom Controller Factory? 

Already you know that Default controller factory creates controller object using parameter less constructor. We can inject our controller by parameterless constructor too. See the code below: 

C#
public class HomeController : Controller
{
    private readonly ILogger _logger;
    public HomeController():this(new DefaultLogger())
    {
    } 
    public HomeController(ILogger logger)
    {
        _logger = logger;
    }
}    

I found many developers who are misguided the way of the above dependency injection process. This is not dependency injection at all. This actually clearly violate the dependency inversion principle. The principle say that "High level module should not depend upon the low level module, both should depend on abstraction. Details should depends upon abstraction". In above code HomeController itself create DefaultLogger object. So it directly depends on ILogger implementation (DefaultLogger). If in future another implementation comes of ILogger interface then HomeController code itself need to change. So it is probed that it is not proper way to do. So if we need proper way to inject dependency. We need parameterised constructor, by which we can inject our ILogger component. So current implementation of DefaultControllerFactory does not support this requirement. So we need a new custom controller factory.  

7. Custom Controller Factory Creation 

I explain 2 approaches here for creating custom controller factory:  

7.1 Approach-1:  

We can create a new custom controller factory by implementing IControllerFactory interface. Suppose our new controller factory named CustomControllerFactory. So its implementation looks like: 

public class CustomControllerFactory : 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();
    }
} 

Now at first 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 the application and see that your parameter-less constructor HomeController is not called, instead of that parameterized HomeController(ILogger logger) constructor is called by MVC framework. Wow! your problem is solved so easily. 

Image 9

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 dependent object through reflection. In current code implementation has some problems, which are:

  1. You need to pass controller namespace (using constructor) so every controller should be same namespace and
  2. All controllers need single parameterized construction which accept ILogger object only. Without that it will throw MissingMethodException.   

7.2 Approach-2 

<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 virtual methods and changed/implement whatever you need. One implementation example might be as follows:  

C#
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 create my own CustomControllerFactory class which is inherited from DefaultControllerFactory and override GetControllerInstance method and implement my custom logic.  

8. Using MEF 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 problems need to raise and handle if you try to dynamically create dependent object and controller object. So it will be better approach to use any IOC container to your project and register all your dependent objects there and use it. You can use various popular IOC containers like Castle Windsor, Unity, NInject, StructureMap etc. I will demonstrate how Managed Extensibility Framework (MEF) is used in this situation. MEF is not an IOC container. It is a composition layer. You can also called it plug-able framework  by which you can plugin  your dependent component at run time. Almost all types of work you can do by MEF which you can do with IOC. When to use MEF when IOC it depends on your requirements, application architecture. In my suggestion is when you need to compose your component at run time( run time plug-in play) in that case you can use MEF, when you create your components with its dependent component with static reference then you can use IOC. There are many articles published online where you can find more details/technical write-up regarding that. I hope you can study more on that and easily take decision which you can use and when. By the way MEF comes with .NET framework 4.0 So main benefit is, no third party component dependency is there. First you take reference of system.ComponentModel.Composition to your project.  

Image 10

Then you create custom Controller Factory. The name of the controller factory is MefControllerFactory

C#
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> 

C#
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 InheritedExportAttributeExportAttribute,  PartCreationPolicyAttribute of MEF to interface ILoggerHomeController.  

10

Image 12

Based on these attributes MEF framework create objects. Just one important think you should remember that when you will use MEF, you should decorate your controllers through PartCreationPolicyAttribute and set controllers life time as create object per request policy.  [PartCreationPolicy (CreationPolicy.NonShared)] Without that you will get an error. By default it will use SharedCreation policy. 

Image 13 

9. Points of Interest  

I tried to explain various ways to create controller factory and inject its dependency in a very simple and straight forward way. 1st and 2nd dependency injection approaches are just for make 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 take any IOC or MEF framework for your real life project. You can do more research on MEF and learn MEF usage and best practices after that you will use it to your real life projects. Near future I have a plan to write another article that will demonstrate to use of various IOC containers like Windsor, Unity, NInject, StrucutureMap etc. to inject controller dependency injection and a comparative study with them.

Sample Source Code

In my sample downloadable source code, I use visual studio 2012 with .NET framework 4.5. Anyone can download and play with it.<o:p> 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Bangladesh Bangladesh
How do I describe myself to you? How can I explain that this is true?
I am who I am because of you! My work I love you !!

Comments and Discussions

 
SuggestionCustom Controller Factory No Longer Required Pin
X-treem19-Sep-18 1:02
X-treem19-Sep-18 1:02 
PraiseGreat Article to Understand DI Pin
Ranjan833112-Feb-18 16:41
Ranjan833112-Feb-18 16:41 
QuestionNice article for understanding about DI Pin
Member 120654932-Sep-16 5:10
Member 120654932-Sep-16 5:10 
PraiseGreat Article to know dependency Injection Pin
mahonealex18-Aug-16 23:54
mahonealex18-Aug-16 23:54 
QuestionA very bug in the code in the article Pin
Rohit Kejriwal10-Apr-16 7:58
professionalRohit Kejriwal10-Apr-16 7:58 
GeneralMy vote of 5 Pin
VSG2426-Oct-15 9:10
VSG2426-Oct-15 9:10 
PraiseFantastic Pin
VSG2426-Oct-15 9:13
VSG2426-Oct-15 9:13 
QuestionGood article Pin
Tridip Bhattacharjee15-Sep-15 8:39
professionalTridip Bhattacharjee15-Sep-15 8:39 
GeneralMy vote of 5 Pin
joyhen12312312-Jul-15 16:50
joyhen12312312-Jul-15 16:50 
GeneralExcellent article Pin
vatsa_gaurav18-Apr-15 9:47
vatsa_gaurav18-Apr-15 9:47 
QuestionGood Work Pin
Imran Abdul Ghani9-Dec-14 20:57
Imran Abdul Ghani9-Dec-14 20:57 
GeneralNice Article Pin
Member 112864464-Dec-14 5:15
Member 112864464-Dec-14 5:15 
QuestionNice Article But... Pin
Ankit Sarkar1-Aug-14 3:18
Ankit Sarkar1-Aug-14 3:18 
QuestionThe link for the source code is broken Pin
Bill Warner10-Jun-14 2:00
Bill Warner10-Jun-14 2:00 
AnswerRe: The link for the source code is broken Pin
S. M. Ahasan Habib10-Jun-14 16:50
professionalS. M. Ahasan Habib10-Jun-14 16:50 
GeneralMy vote of 5 Pin
Paul Pacurar22-Jan-14 5:06
Paul Pacurar22-Jan-14 5:06 
GeneralRe: My vote of 5 Pin
S. M. Ahasan Habib22-Jan-14 7:10
professionalS. M. Ahasan Habib22-Jan-14 7:10 
GeneralMy vote of 5 Pin
Champion Chen5-Jan-14 2:56
Champion Chen5-Jan-14 2:56 
GeneralRe: My vote of 5 Pin
S. M. Ahasan Habib5-Jan-14 5:02
professionalS. M. Ahasan Habib5-Jan-14 5:02 
GeneralMy vote of 5 Pin
Jameel VM1-Jan-14 19:40
Jameel VM1-Jan-14 19:40 
GeneralRe: My vote of 5 Pin
S. M. Ahasan Habib1-Jan-14 19:59
professionalS. M. Ahasan Habib1-Jan-14 19:59 
thanks!
GeneralMy vote of 5 Pin
Akhil Mittal1-Jan-14 16:50
professionalAkhil Mittal1-Jan-14 16:50 
GeneralRe: My vote of 5 Pin
S. M. Ahasan Habib1-Jan-14 17:16
professionalS. M. Ahasan Habib1-Jan-14 17:16 
QuestionLooking forward to your CastleWindsor-specific article Pin
B. Clay Shannon31-Dec-13 10:56
professionalB. Clay Shannon31-Dec-13 10:56 
AnswerRe: Looking forward to your CastleWindsor-specific article Pin
S. M. Ahasan Habib31-Dec-13 15:11
professionalS. M. Ahasan Habib31-Dec-13 15:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.