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

Dependency Injection Scenarios in ASP.NET MVC

Rate me:
Please Sign up or sign in to vote.
4.18/5 (9 votes)
16 Jun 2016CPOL8 min read 58K   16   19
A couple of Dependency Injection scenarios in ASP.NET MVC
In this article, you will find examples of DI in different areas of ASP.NET MVC application architecture. There will be partial code demonstration here and the entire example could be downloaded from GitHub.

Introduction

Dependency injection is a programming and application design pattern which is being used by developers for so many years now. This pattern comes as a default programming syntax in many frameworks like Angular and we must follow them in order to use the framework. I will not go much into explaining what Dependency Injection actually is because there is much information available on the subject which could be accessed using a simple web search. Here is the formal definition from Wikipedia:

Quote:

In software engineering, dependency injection is a software design pattern that implements inversion of control for resolving dependencies. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it.

Dependency Injection promotes loose coupling between application modules and provides us means to switch code modules being used without building and redeploying the entire application or parts of it. This could be done by using either application configuration file or by reading dependency information from a database table.

We can create a DI code framework of our own for custom scenarios. But in ASP.NET MVC, in order to use DI in certain areas, we must follow the .NET approach. This could be done by implementing in-built interfaces into classes and wiring them up from Global.asax when the application starts.

I am assuming that you are aware of dependency injection as a design pattern and as a programming concept before you start reading further. In this article, I will be giving examples of DI in different areas of ASP.NET MVC application architecture. There will be partial code demonstration in this article and the entire example could be downloaded from either here or from GitHub where I have uploaded it in a public repository.

Using the Code

Create an empty ASP.NET MVC application. Add a single HomeController, Index view and a Web API Controller.

Controller Class Dependency Injection

Controller classes are extensively used in the ASP.NET MVC from simply returning the HTML view to the browser to providing Get and Post methods which could be invoked from the client side. So many times, we need to do things like data access, error logging, caching, etc. from a controller class. Now there are a couple of ways to do this. The standard way is to just create an object out of the manager class which contains the data access logic and use it directly. But there are several issues in this approach.

The manager class will be tightly bound to the controller class and in order to use a different manager class implementation, we would have to make changes to the controller code and re-deploy the application after building it. There could be several such classes that the controller class could need to perform the essential business logic operations which means more changes.

DataManager Class

C#
namespace DIScenarios.Managers
{
    public class DataManager
    {
        public Object GetData()
        {
            throw new NotImplementedException();
        }
    }
}

This could be handled by dynamically providing the controller class its dependencies whenever an object is created out of it. For this purpose, we can inject the required dependency into the constructor of the controller class. Now the question arises that from where can we actually inject the dependency. To do this, we will need to implement the System.Web.Mvc.DefaultControllerFactory class which inherits its class contract from IControllerFactory interface.

The DefaultControllerFactory will be extended into the ControllerFactory class. In the ControllerFactory, we need to implement the GetControllerInstance() method. Based on the type of controller being requested, we can return appropriate controller object by injecting the required dependency into its constructor method.

C#
namespace DIScenarios.Controllers
{
    public class HomeController : Controller
    {
        private DataManager _manager;
        public HomeController(DataManager manager) 
        {
            _manager = manager;
        }

        /// <summary>
        /// Returns the Index view.
        /// </summary>
        /// <returns></returns>
        public ActionResult Index()
        {
            //using the injected manager dependency to return the object model to the view.
            return View(_manager.GetData());
        }
    }
}

The question arises that how will the ControllerFactory get the reference of the dependency which it will later insert into the controller. For this, we will again use constructor injection pattern; create a ControllerFactory constructor and have an argument of the manager class. The constructor will set the given argument into a private manager member.

C#
namespace DIScenarios
{
    public class ControllerFactory : DefaultControllerFactory
    {
        private DataManager _manager;
        public ControllerFactory(DataManager manager)
        {
            _manager = manager;
        }

        protected override IController GetControllerInstance
         (System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            return controllerType == typeof(HomeController) ? 
                                     new HomeController(_manager) : null;
        }
    }
}

The ControllerFactory can finally be used in the Global.asax file. We will have to use the ControllerBuilder class to dynamically create the controllers and inject the required dependency into them. After everything is done, we can finally run the application and can see the dependency object injected into the controller class. You can see everything in action in the attached code.

C#
namespace DIScenarios
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            DataManager dataManager = new DataManager();

            #region Controller Dependency Injection

            ControllerBuilder.Current.SetControllerFactory(new ControllerFactory(dataManager));

            #endregion Controller Dependency Injection            
        }
    }
}

The ControllerFactory basically acts in the middle of the controller object's request and response. It allows us to do loads of other stuff before we can actually create the controller class object and return it to be used by the ASP.NET MVC.

This was all about injecting dependencies into the controller class. There is another area in which we need to follow the .NET practices to inject objects and that is the Web API controller class. Web API dependency injection works almost the same as the controller injection which you will see in the next section.

Web API Controller Class Injection

To pass dependencies into the Web API controller, we will have to follow the same design pattern and that is to pass the dependency reference into the constructor function. The constructor will then set the reference into a private member and this will allow us to use it from anywhere inside the API controller class object.

C#
namespace DIScenarios.ApiControllers
{
    public class ValuesController : ApiController
    {
        private DataManager _manager;
        public ValuesController(DataManager manager)
        {
            _manager = manager;
        }

        // GET api/<controller>
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/<controller>/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/<controller>
        public void Post([FromBody]string value)
        {
        }

        // PUT api/<controller>/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/<controller>/5
        public void Delete(int id)
        {
        }
    }
}

We used IControllerFactory interface implementation to pass along the dependency to the controller class when the application starts. For Web API, we will need to implement the IDependencyResolver interface and specifically the GetService() method. We will again have to do type checking like we did in the previous section and will create the appropriate API controller class object along with passing the dependency object.

The IDependencyResolver interface contract will be implemented into a WebApiDependencyResolver class and it will have a constructor function to get the dependencies which it will then pass along into various API controller classes.

C#
namespace DIScenarios
{
    public class WebApiDependencyResolver : IDependencyResolver
    {
        private DataManager _manager;

        public WebApiDependencyResolver(DataManager manager)
        {
            _manager = manager;
        }

        public Object GetService(Type serviceType)
        {
            return serviceType == typeof(ValuesController) ? 
                                  new ValuesController(_manager) : null;
        }

        public IEnumerable<Object> GetServices(Type serviceType)
        {
            return new List<Object>();
        }

        public IDependencyScope BeginScope()
        {
            return this;
        }

        public void Dispose()
        {

        }
    }
}

In the above, the code is first checking the type of API controller object requested and it is injecting the manager object dependency into the constructor.

Like the ControllerFactory, the WebApiDependencyResolver also works in the middle of API controller request and response pipeline. We have to implement the WebApiDependencyResolver class in the Global.asax file like we did in the case of the ControllerFactory class. For this purpose, we will have to use the GlobalConfiguration object; we can set the DependencyResolver property of GlobalConfiguration with our own custom API dependency resolver. This has been done in the following code:

C#
namespace DIScenarios
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            
            DataManager dataManager = new DataManager();
            #region Web Api Dependency Injection

            GlobalConfiguration.Configuration.DependencyResolver = 
                                          new WebApiDependencyResolver(dataManager);

            #endregion Web Api Dependency Injection
        }
    }
}

When the application is run, then whenever the API controller is called from the client, the ASP.NET MVC will use our custom dependency resolver to create the API controller object and would then be able to inject the required dependency into the object made out of the API controller class.

Controlling Injection Using Configuration Settings

We have seen in the above sections how we can inject dependencies into the MVC controller and API controller in the simplest manner. But the above implementation is still not dynamic. Suppose we need to update the type of manager class implementation being used, then we would need to modify the code in several places to use the new manager class implementation. This is a big setback because every time we change the code, we would need to rebuild and deploy the entire application or at least some parts of it.

This problem could be resolved by reading the dependency information from the application configuration. Now this configuration could be either in an external config file or it could be in the database. In any case, if we decide to change the dependency information then the entire application will need to be restarted but it is less disruptive than redeploying the entire thing again. At least we would not have to do the integration testing between the dependency and the controller class.

Let us now see how to do this implementation. We will need to read the dependency information and then based on that, the code will create an object dynamically and will insert it into to the constructor. In the configuration, we will store the name of the injectable manager class in text form along with its entire namespace information.

XML
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    ...
    <add key="DataManagerClass" value="DIScenarios.Managers.DataManagerMySQL" />
  </appSettings>

  <system.web>    
    ...
  </system.web>

  <system.webServer>
    ...
  </system.webServer>
</configuration>

Now in the controller, ControllerFactory and Global.asax, we will need to use an interface which will enforce common implementation in all the different manager classes. We will be injecting the dependency in the form of the interface so that we won't need to worry about the specific type of manager class.

For this, let's add a new interface which we will use to inject the dependency and it will also act as the class contract for the manager class.

IDataManager

C#
namespace DIScenarios.Interfaces
{
    public interface IDataManager
    {
        Object GetData();
    }
}

Let's also create another manager class implementation which we can switch to as per our needs in the config file.

DataManagerMySQL

C#
namespace DIScenarios.Managers
{
    public class DataManagerMySQL : IDataManager
    {
        public Object GetData()
        {
            throw new NotImplementedException();
        }
    }
}

Following changes are needed to be made in all the classes:

DataManager

C#
namespace DIScenarios.Managers
{
    public class DataManager : IDataManager
    {
        public Object GetData()
        {
            throw new NotImplementedException();
        }
    }
}

The normal DataManager will now simply inherit from the IDataManager interface.

HomeController

C#
public class HomeController : Controller
{
    private IDataManager _manager;
    public HomeController(IDataManager manager)
    {
        _manager = manager;
    }
}

ControllerFactory

C#
public class ControllerFactory : DefaultControllerFactory
{
    private IDataManager _manager;
    public ControllerFactory(IDataManager manager)
    {
        _manager = manager;
    }
}

ValuesController

C#
namespace DIScenarios.ApiControllers
{
public class ValuesController : ApiController
{
    private IDataManager _manager;
    public ValuesController(IDataManager manager)
    {
        _manager = manager;
    }

}

WebApiDependencyResolver

C#
public class WebApiDependencyResolver : IDependencyResolver
{
    private IDataManager _manager;

    public WebApiDependencyResolver(IDataManager manager)
    {
        _manager = manager;
    }
}

As you can see in the above code, we are now using IDataManager interface instead of the DataManager class. We can anytime switch between normal DataManager and DataManagerMySql in the config file.

In the global.asax file, the dependency object will be created in the following way after reading its class name from the config file:

Global.asax.cs

C#
namespace DIScenarios
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            #region Figure out the type of dependency to use from the 
                    application configuration file

            String type = WebConfigurationManager.AppSettings["DataManagerClass"];
            IDataManager dataManager = Activator.CreateInstance
               (System.Reflection.Assembly.GetExecutingAssembly().GetName().Name, type)
               .Unwrap() as IDataManager;

            #endregion Figure out the type of dependency to use from the 
                       application configuration file

            #region Controller Dependency Injection

            ControllerBuilder.Current.SetControllerFactory(new ControllerFactory(dataManager));

            #endregion Controller Dependency Injection

            #region Web Api Dependency Injection

            GlobalConfiguration.Configuration.DependencyResolver = 
                                     new WebApiDependencyResolver(dataManager);

            #endregion Web Api Dependency Injection
        }
    }
}

In the above code, Activator.CreateInstance() is being used to create the manager class object using the class name. The type of object created will have no impact on the functionality as we are now using interface instead of a class inside the controllers.

This was all about dependency injection in this article. The code is as simple as it can get but for production environment, you might want to do much more like adding validations, more complex type checking, etc.
Feel free to ask any questions or provide any constructive suggestions.

History

  • 16th June, 2016: Initial version

License

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


Written By
Software Developer (Senior)
India India
Just a regular guy interesting in programming, gaming and a lot of other stuff Smile | :)

Please take a moment to visit my YouTube Channel and subscribe to it if you like its contents!
My YouTube Channel

Don't be a stranger! Say Hi!!

Cheers!

Comments and Discussions

 
QuestionWhat's about IoC Containers? Pin
Michael Freidgeim21-Dec-16 10:03
Michael Freidgeim21-Dec-16 10:03 
The article doesn't mention IoC Containers. Why should I write dependency injection factory by myself instead of using one of many containers, available on NuGet?
Michael Freidgeim.
Blog: http://mfreidge.wordpress.com

QuestionDI has its place but needs to stay there... Pin
Member 43662206-Jul-16 23:16
Member 43662206-Jul-16 23:16 
QuestionControllers that doesn't need DI Pin
Luis M. Teijón25-Jun-16 7:08
Luis M. Teijón25-Jun-16 7:08 
AnswerRe: Controllers that doesn't need DI Pin
Nitij27-Jun-16 20:13
professionalNitij27-Jun-16 20:13 
GeneralRe: Controllers that doesn't need DI Pin
Luis M. Teijón6-Jul-16 17:57
Luis M. Teijón6-Jul-16 17:57 
QuestionThe Overuse of Dependency Injection Pin
Chris Solutions17-Jun-16 11:15
professionalChris Solutions17-Jun-16 11:15 
AnswerRe: The Overuse of Dependency Injection Pin
Nitij17-Jun-16 21:30
professionalNitij17-Jun-16 21:30 
GeneralRe: The Overuse of Dependency Injection Pin
Chris Solutions18-Jun-16 17:38
professionalChris Solutions18-Jun-16 17:38 
GeneralRe: The Overuse of Dependency Injection Pin
Nitij19-Jun-16 21:28
professionalNitij19-Jun-16 21:28 
GeneralRe: The Overuse of Dependency Injection Pin
John Brett22-Jun-16 1:49
John Brett22-Jun-16 1:49 
AnswerRe: The Overuse of Dependency Injection Pin
John Brett20-Jun-16 0:12
John Brett20-Jun-16 0:12 
GeneralRe: The Overuse of Dependency Injection Pin
Chris Solutions21-Jun-16 19:11
professionalChris Solutions21-Jun-16 19:11 
GeneralRe: The Overuse of Dependency Injection Pin
John Brett22-Jun-16 2:01
John Brett22-Jun-16 2:01 
GeneralRe: The Overuse of Dependency Injection Pin
Chris Solutions22-Jun-16 13:53
professionalChris Solutions22-Jun-16 13:53 
GeneralRe: The Overuse of Dependency Injection Pin
John Brett23-Jun-16 4:50
John Brett23-Jun-16 4:50 
GeneralRe: The Overuse of Dependency Injection Pin
John Brett23-Jun-16 5:04
John Brett23-Jun-16 5:04 
GeneralRe: The Overuse of Dependency Injection Pin
John Brett22-Jun-16 2:03
John Brett22-Jun-16 2:03 

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.