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 example

    Posted by Srinivas on 07/03/2014 04:07am

    The best example I have seen ever... I loved it..

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

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • CentreCorp is a fully integrated and diversified property management and real estate service company, specializing in the "shopping center" segment, and is one of the premier retail service providers in North America. Company executives travel a great deal, carrying a number of traveling laptops with critical current business data, and no easy way to back up to the network outside the office. Read this case study to learn how CentreCorp implemented a suite of business continuity services that included …

Most Popular Programming Stories

More for Developers

RSS Feeds