Understanding Dependency Injection

Introduction

If you ever developed ASP.NET MVC applications you probably have come across this term - Dependency Injection. Dependency Injection is a way to implement the Dependency Inversion Principle. Simply put, Dependency Injection is a design pattern that helps a class separate the logic of creating dependent objects. The result of this separation is a loosely coupled system where there is no rigid dependency between two concrete implementations. This article discusses what Dependency Injection is and illustrates its use in an ASP.NET MVC application.

Note:
Although this article illustrates Dependency Injection using ASP.NET MVC, the underlying principle can be applied to any platform or programming framework. Also keep in mind that this article is intended to explain the Dependency Injection principle and not its specific implementation under ASP.NET MVC. Hence topics such as Containers are beyond the scope of this article.

What is Dependency Injection?

Dependency Injection (DI) is a design pattern that takes away the responsibility of creating dependencies from a class thus resulting in a loosely coupled system. In order to understand DI you need to be aware of the following terms:

  • Dependency
  • Dependency Inversion Principle
  • Inversion of Control (IoC)

Let's assume that you have a simple C# class as shown below:

public class Customer
{
  private DatabaseHelper helper;

  public Customer()
  {
    helper = new DatabaseHelper();
  }
  ...
  ...
}

The Customer class declares a variable of type DatabaseHelper. The DatabaseHelper class is supposed to be doing some database operations such as SELECT and UPDATE. The constructor of the Customer class instantiates the DatabaseHelper class and the instance is stored in the helper variable. In this case the Customer class is dependent on the DatabaseHelper class for its functioning. Thus DatabaseHelper is a dependency of the Customer class.

The problem with the above design is that the Customer class and DatabaseHelper class are tightly coupled. If you ever need to substitute DatabaseHelper with some other class (say XMLHelper) then you will have to change the code of the Customer class since it directly instantiates the DatabaseHelper. To help avoid this, tight coupling Dependency Inversion Principle (DIP) is used. The DIP states that - High-level modules should not depend on low-level modules. Both should depend on abstractions. That means Customer should not depend on a concrete implementation (DatabaseHelper) but rather should depend on an abstraction. At code level this means the Customer class won't have a variable of DatabaseHelper type, instead it will have a variable of some interface (or abstract class) type. This is shown below:

public class Customer
{
  private IStorageHelper helper;

  public Customer()
  {
    helper = new DatabaseHelper();
  }
  ...
  ...
}

Now, the Customer class uses a variable of type IStorageHelper interface everywhere. IStorageHelper is supposed to be an interface that is implemented by the DatabaseHelper class and all such classes. Thus most of the code of the Customer class is now using an abstraction in the form of IStorageHelper.

Although the above code is better than the original implementation, it still has a problem. The helper variable is still instantiated inside the Customer class. This problem arises because the Customer class is responsible for creating its dependency (DatabaseHelper). The Inversion of Control principle (IoC) comes in handy in such cases. IoC states that the control of creating the decencies should be with the external system rather than the class itself. In the above example this means that the Customer class shouldn't create an instance of DatabaseHelper, rather it should be received from the external system.

Dependency Injection is a way to implement IoC such that the dependencies are "injected" into a class from some external source. The injected dependencies can either be received as constructor parameters of a class or can be assigned to properties of that class designed for that purpose. The former approach is commonly used in ASP.NET MVC. So, the above code now becomes:

public class Customer
{
  private IStorageHelper helper;

  public Customer(IStorageHelper helper)
  {
    this.helper = helper;
  }
  ...
  ...
}

As you can see, the helper instance is now coming from the external world and is being injected into the Customer class through its constructor.

Applying Dependency Injection in ASP.NET MVC

Now that you understand the basics of Dependency Injection, let's see how DI can be used in ASP.NET MVC controllers. Consider the following controller class:

public class HomeController : Controller
{
  ICustomerRepository repository = null;

  public HomeController(ICustomerRepository repository)
  {
    this.repository = repository;
  }

  public ActionResult Index()
  {
    List<CustomerViewModel> data = repository.SelectAll();
    return View(data);
  }
}

The above code should be immediately familiar to you because it uses the concept of IoC and DI as discussed in the previous section. The HomeController class declares a variable of ICustomerRepository. The ICustomerRepository is an interface designed to implement the Repository pattern and is shown below:

public interface ICustomerRepository
{
  List<CustomerViewModel> SelectAll();
  CustomerViewModel SelectByID(string id);
  void Insert(CustomerViewModel obj);
  void Update(CustomerViewModel obj);
  void Delete(CustomerViewModel obj);
}

We won't go into the details of Repository pattern here. Just in case you are not familiar with the Repository pattern read this article to learn more.

In the above code some implementation of ICustomerRepository is injected inside the HomeController through its constructor. As you can see the Index() action method calls the SelectAll() method on the repository to retrieve all the customers as a List of CustomerViewModel objects. The CustomerViewModel is a simple class that acts as a view model and is shown below:

public class CustomerViewModel
{
  public string CustomerID { get; set; }
  public string CompanyName { get; set; }
  public string ContactName { get; set; }
  public string Country { get; set; }
}

Although the above code seems to apply DI as expected there is a problem. The problem is that while instantiating the HomeController for processing the incoming requests, ASP.NET MVC framework uses the parameter less constructor of the HomeController. Since our HomeController no longer has one, the above code throws an exception at runtime. Luckily, ASP.NET MVC allows you to explicitly specify how controllers should be instantiated. This can be done by creating your own Controller Factory.

A controller factory is a class that usually inherits from DefaultControllerFactory class and is responsible for creating controller instances. Once created you need to register your custom controller factory with the ASP.NET MVC framework so that it can use your controller factory instead of the default one. Let's create the custom controller factory first. Have a look at the following code:

public class MyControllerFactory:DefaultControllerFactory
{
  private Dictionary<string, Func<RequestContext, IController>> controllers;

  public MyControllerFactory(ICustomerRepository repository)
  {
    controllers = new Dictionary<string, Func<RequestContext, IController>>();
    controllers["Home"] = controller => new HomeController(repository);
  }

  public override IController CreateController(RequestContext requestContext, string controllerName)
  {
    if(controllers.ContainsKey(controllerName))
    {
      return controllers[controllerName](requestContext);
    }
    else
    {
      return null;
    }
  }
}

The MyControllerFactory class inherits from the DefaultControllerFactory class provided by the ASP.NET MVC framework. The constructor of MyControllerFactory accepts an instance of ICustomerRepository. This way the MyControllerFactory doesn't depend on any concrete implementation of ICustomerRepository. The code then overrides the CreateController() method of the base class. A Dictionary objects maintains a list of controllers in the application. Notice that HomeController is being instantiated in the constructor and is stored with a key of Home. The CreateController() simply returns this instance of HomeController from the Dictionary.

Now that the custom controller factory is ready, you need to register it with the ASP.NET MVC framework. To help you with this registration we create a helper class - ControllerFactoryHelper - as shown below:

 public class ControllerFactoryHelper
{
  public static IControllerFactory GetControllerFactory()
  {
    string repositoryTypeName = ConfigurationManager.AppSettings["repository"];
    var repositoryType = Type.GetType(repositoryTypeName);
    var repository = Activator.CreateInstance(repositoryType);
    IControllerFactory factory = new MyControllerFactory(repository as ICustomerRepository);
    return factory;
  }
}

The ControllerFactoryHelper class has just one static method, GetControllerFactory(). The job of GetControllerFactory() is to instantiate MyControllerFactory and prepare for the registration. This is the place where the concrete implementation of ICustomerRepository are required. This is so because unless these details are known you can't instantiate MyControllerFactory (since the constructor of MyControllerFactory needs an object implementing ICustomerRepository). The above code assumes that these details are stored in the <appSettings> section of the web.config file as shown below:

 <appSettings>
  ...
  <add key="repository" value="DIDemo.Repositories.CustomerRepository"/>
</appSettings>

The GetControllerFactory() reads the repository key and creates an instance of DIDemo.Repositories.CustomerRepository using Reflection. It then instantiates MyControllerFactory by passing this object. The factory object is then returned to the caller.

Now comes the final step. To register the custom controller factory you need to add the following code in Globa.asax:

 protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
  RouteConfig.RegisterRoutes(RouteTable.Routes);

  ControllerBuilder.Current.SetControllerFactory(ControllerFactoryHelper.GetControllerFactory());
}

The Application_Start event handler uses the ControllerBuilder instance and calls its SetControllerFactory() method. The factory object returned by the GetControllerFactory() static method is passed as a parameter to the SetControllerFactory() method.

That's it! The following figure shows a sample run of the Index view.

Index View

Summary

Dependency Injection is a technique to separate the creation of dependencies from the main class under consideration. Using DI you inject the objects needed  by a class typically through a constructor. This article illustrated how DI can be used in ASP.NET MVC controllers. For the sake of simplicity it didn't use any IoC Container frameworks such as Unity, Castle Windsor and NInject. In real world applications you may use one of such frameworks to make your job easier.



Related Articles

Downloads

Comments

  • Awesome

    Posted by Rohit on 07/22/2015 02:26pm

    Loved the explaination...

    Reply
  • Simple, Elegant, and Descriptive

    Posted by RichP on 07/06/2015 10:18am

    Best example I've seen for where to assign the concrete implementation of the repository. Every other tutorial seems to stop short of this crucial step. Many thanks!

    Reply
  • All time best

    Posted by Pradeep on 07/03/2015 10:21am

    I was struggling over the internet to understand the concept of DI. I could not grasp the concept from any of them including microsoft articles. This is truly the best one...hats off to you sir!!!

    Reply
  • Excellent

    Posted by Satish Rasal on 04/08/2015 02:43am

    Excellent explaination

    Reply
  • DI made simple

    Posted by Shyam on 04/05/2015 07:56am

    This is a nice article which explains DI step-by-step in simple way.

    Reply
  • Great overview

    Posted by Jon Potter on 04/01/2015 12:39pm

    This is a very nice explanation for the parts of Dependency Injection, and I especially like the inclusion of the factory. Many examples I see leave you at Poor Man's DI or straight to an IoC container.

    Reply
  • Well organised

    Posted by Venkat Yamasani on 02/17/2015 01:02am

    Really good article and the way of organising the steps

    Reply
  • Understanding Dependency Injection with Example

    Posted by Madhu Bysani on 01/19/2015 10:11pm

    Understanding Dependency Injection

    Reply
  • By far the best example of DI

    Posted by Santosh Navle on 01/09/2015 03:47am

    Appreciated the way the whole example was explained with simple steps and easily understandable by the beginners.

    Reply
  • Excellent

    Posted by Kiran Bangalore on 01/07/2015 04:42am

    Really good explanation on DI. I searched on so many sites including msdn. Finally understood the concept here. Thanks a lot sir.

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • As the mobile enterprise marketplace expands and customer needs grow more diverse, Samsung recognizes that solution partners and developers play an essential role by continually innovating to meet their customers' needs. Samsung works to provide these developers and partners with the latest tools and resources needed to create these solutions. Read this program guide to learn how the Samsung Enterprise Alliance Program provides partners and developers with Samsung enterprise software development kits (SDKs) …

  • Moving from an on-premises environment to Office 365 does not remove the need to plan for disruptions or reduce the business risk requirements for protecting email services. If anything, some risks increase with a move to the cloud. Read how to ease the transition every business faces if considering or already migrating to cloud email. This white paper discusses: Setting expectations when migrating to Office 365 Understanding the implications of relying solely on Exchange Online security Necessary archiving …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date