Click here to Skip to main content
13,198,727 members (41,499 online)
Click here to Skip to main content
Add your own
alternative version

Stats

13.1K views
152 downloads
17 bookmarked
Posted 24 Jan 2016

Dependency Injection : How Exactly Inversion of Control Takes Place

, 26 Jan 2016
Rate this:
Please Sign up or sign in to vote.
Dependency injection, DI containers, IoC containers and Inversion of control are a world of confusion. Let's take them head-on.

Introduction

This is the second portion of my two part article in which overall I'm trying to cover some ground on DIP, DI, IoC, DI containers and IoC containers in totality. I'll be using many terminologies from the first part of this series, so it is important that you go through my first article which will make you understand the gist of Dependency Inversion Principle (DIP). It is available at the following link:

Here is the agenda of my current article:

  1. What is Dependency Injection? It's plain vanilla implementation. Understand that Dependency Inversion Principle is NOT Dependency Injection though they share same initials. It is simply NOT. Vice versa is obviously true as well.
  2. Understand that Dependency Injection can be implemented even if your modules and components don't follow or exhibit the Dependency Inversion principle.
  3. How Dependency injection (DI) and Dependency inversion principle (DIP) working together produce the best solution.

A Note for the Attached Code

You need the following software as prerequisites to run the attached code on your computer:

  1. Visual Studio 2010 or above
  2. Microsoft SQL Server - even an Express edition would be enough

The attached code contains a file "DatabaseScripts.sql" which you will need to run on your local SQL Server instance to run the code without error. In case you do not have SQL Server on your machine, still you will be able to understand the code. To run the code without SQL Server, you will have to comment a few lines of code which does the task of saving records into database present in "SaveDataToStorage" function. "SaveDataToStorage" function is present in "EmployeeComponentWithConstructorDI.cs" and "EmployeeComponentWithoutDIP.cs" code files. Line numbers are 66 and 67 which should be commented in such a case.

Background : Customer's Requirement

Here is a brief history of the problem I'm facing or trying to solve for all of us. My customer told me that there should be an employee entity which holds basic attributes of an employee like name and salary. It should also expose a public interface which can be invoked to save the current attributes of the employee to some persistent medium for e.g. database, text file on disk, etc. Customer told me that currently I'm using SQL database as persistent medium where employee details would be saved. Sweet and simple requirement! Isn't it?

Recap of the Initial Story from Part I

The biggest problem that every developer faces is when he has to CHANGE his software for business'/customer's (changing) needs. Developer wants to minimize the changes in his software to zero if possible for changing requirements by following best coding practices. So one fine day, my customer comes with a change in his requirements: "SQL server licenses are a bit costlier to me so I'm moving Microsoft Access based database storage which is comparatively a cheaper option. So I want you to save all the employee information to Microsoft Access instead of Microsoft SQL Server". I was tasked with changing the employee class to accomplish this requirement. Now I got the idea that my customer has this habit of changing the persistent medium where he wants us to save employee details. There is every possibility in future that he will try to use an even cheaper or different option and change the persistent medium. I started thinking of coming up with a generalized solution which will cater to this changing need so that every time my customer comes with such a new persistent storage idea, I've to make minimal or literally no changes in my core component. So my guru uncle bob told me that you will be able to deal with such problems if your module/class conform to Dependency Inversion principle (a tenet of SOLID design principles). So I gave it a shot and tried to fine tune my module as discussed in the previous part of this article which can be found here. Without understanding dependency inversion principle, it will be hard for you to understand dependency injection design pattern, so I again insist you to go through my previous article.

Quick Introduction To A Basic Concept

I want to introduce you to a term real quick before we talk further as I will be using this term like hell in this article.

Dependency: I referred to this same concept as "Low Level module" in my previous article. Essentially, a dependency is any class, module or component to which your current component delegates some responsibilities to get a work done. Have a look at the below code snippet # 1 referred from my previous article. Here, employee class uses "DatabaseStorageAgentForSqlServer" class to delegate the work of saving employee details to a persistent storage. So "DatabaseStorageAgentForSqlServer" class is a dependency for "Employee" class. "Employee" class is dependent upon "DatabaseStorageAgentForSqlServer" class for getting some work done.

Code Snippet # 1

using System.Data;
using System.Data.SqlClient;
using System.Data.OleDb;

namespace DependencyInversionPrincipleGenericStorageImplementation
{
    public class Employee
    {
        //Trivial code has been removed for brevity and clarity. 
        //Please download attached code from my previous article of this series for complete reference

        public void SaveEmployeeDetails()
        {
            //Instantiate low-level Dependency
            IStorageService persistanceStorageAgent = new DatabaseStorageAgentForSqlServer();
            if (IsValidEmployee(empName,salary))
            {
                //save employee details
                persistanceStorageAgent.SaveData(empName, salary);
            }
        }
    }

    public interface IStorageService
    {
        void SaveData(string name, int salary);
        
    }

    public class DatabaseStorageAgentForSqlServer : IStorageService
    {
        public void SaveData(string name, int salary)
        {
            //create query text
            string queryText = string.Format
            ("insert into Employee (Name,Salary) values ('{0}',{1})", name, salary);

            //create persistence storage connection
            var oleDbCon = GetPersistantStorageConnection();

            //save employee details into the persistence storage
            SaveDataToStorage(oleDbCon, queryText);
        }
        private IDbConnection GetPersistantStorageConnection()
        {
            return new SqlConnection("Data Source=(local);
            Initial Catalog=EmployeeDB;Integrated Security=SSPI;");
        }

        private void SaveDataToStorage(IDbConnection dbConnection, string queryText)
        {
            var sqlCmd = new SqlCommand(queryText, (SqlConnection)dbConnection);
            dbConnection.Open();
            sqlCmd.ExecuteNonQuery();
        }
    }
}

The Problem Solved in Article Part I

I was trying to make the dependency inside my class as seamlessly pluggable/replaceable/changeable as possible so that my employee component has to suffer least changes when dependency changes from SQL server based implementation to Microsoft access based implementation to any other implementation for persistent storage. As you can see in code snippet # 1 above, to switch my dependency, I simply have to change one line of code where my dependency is getting instantiated inside SaveEmployeeDetails function. I brought the changes happening in my class to minimum. To understand the relevance of IStorageService interface, you will have to understand dependency inversion principle in my previous article. But what problems still remain?

The Left Over Problem Statement

Yes. My component still suffers with issues as detailed below:

  1. Maximum pluggability not achieved: Let's accept the fact that 100% pluggability is not possible to achieve. Whenever you change your software to incorporate customer changes, you will have to make changes to your code at some or the other place. But our intention should be that the changes are minimum, centralised at a single place, cause minimum impact and involve least amount of retesting your software. My dependencies are still NOT fully pluggable/replaceable. Even though I have to change only one line of code to change my dependency implementation for persistent storage, the change still remains. This change will invite rebuilding and redeployment of my component/assembly in production environment.
  2. Case of high number of dependencies: What if my "Employee" class has numerous dependencies. Let's say it has four to five other dependencies to which "Employee" class delegates other work to be done. In that case, there will be multiple points of change. If all five dependencies change, then I will have to change the instantiation points of all five dependency objects. So essentially, this is lot of change which I was initially trying to minimize or remove in entirety.
  3. Dependency object life time management: Yes. This is an issue which we are not able to observe in a direct way, but of course whatever dependency instances you create, then you are responsible to manage them, then you dispose them off when not needed. Your own employee component/class is responsible for doing it. Frustrating, isn't it? You are over burdening your component with multiple responsibilities.
  4. Unit testability of my component: Whenever I have to test my employee component, I have hard dependency on functioning of underlying persistent storage medium, i.e. SQL Server. If by any chance SQL Server is not working, then I can't unit test the logic of my employee component. This essentially means I have no way to mock the external resources or replace the external dependencies at run time to limit the boundary of testing to my component. This point might be little tough to digest right now but should become clear by the end of the article. There is a whole world of mocking frameworks invented to accomplish this need.

So what is the way out gentlemen to get rid of the above listed issues?

The Solution : Dependency Injection Design Pattern

Yes. There is a design pattern to this recurring problem that developers face day in and day out. How to manage your dependencies? How to manage the life time of our dependencies? How to minimize change when my dependencies change? So essentially, how do I design my component so that my "Employee" class doesn't have to instantiate or manage dependencies. Of course, then if you want to let go of these responsibilities, then someone else will have to take it up on your component's behalf. There will be an external/third party component (we will talk about it very soon) that will create and manage your dependencies. So if someone else does that for you, then there should be a way so that, that external agency can pass-on those dependency objects to you for use. That way of passing dependency objects is called INJECTION. The external agency will inject the dependency objects into your class/component. Once you are done using your dependency object, you don't have to bother about its life time management. It will be taken care of by the external agency that created your dependencies. Let me try to implement it in a very basic way where I would hold my main method responsible for dependency management and injection. Let's see how the structure of our "Employee" class also changes to conform to Dependency injection design pattern.

Code Snippet # 2

Program.cs in ClientAppWithoutDIContainer.csproj:

using System;
namespace DependencyInjection
{
    class Program
    {
        static void Main(string[] args)
        {
            //create dependency instance
            IStorageAgent sqlServerPersistanceStorageAgent = new DatabaseStorageAgent();
            //inject dependency through constructor injection
            var employee = new Employee(sqlServerPersistanceStorageAgent);
            employee.EmployeeName = "Rasik";
            employee.Salary = 200;
            employee.SaveEmployeeDetails();
            //Nullify the dependency instance
            sqlServerPersistanceStorageAgent = null;
            Console.WriteLine("Dependency Injection accomplished with 
            main method acting as DI container. Press enter to end the program.");
            Console.ReadLine();
        }
    }
}

EmployeeComponentWithConstructorDI.cs

using System.Data;
using System.Data.SqlClient;

namespace DependencyInjection
{
    public class Employee
    {
        private string empName;
        private int salary;
        IStorageAgent persistanceStorageDependency;

        public Employee(IStorageAgent persistanceStorageAgent)
        {
            //Assigning the injected dependency to local reference.
            persistanceStorageDependency = persistanceStorageAgent;
        }

        public string EmployeeName
        {
            get { return empName; }
            set { empName = value; }
        }
        public int Salary
        {
            get { return salary; }
            set { salary = value; }
        }
        public void SaveEmployeeDetails()
        {
            if (IsValidEmployee(empName,salary))
            {
                //create query text
                string queryText = string.Format
                ("insert into Employee (Name,Salary) values ('{0}',{1})", empName, salary);

                //create persistence storage connection
                var connection = persistanceStorageDependency.GetPersistantStorageConnection();

                //save employee details into the persistence storage
                persistanceStorageDependency.SaveDataToStorage(connection, queryText);
            }
        }
        private bool IsValidEmployee(string inputName, int inputSalary)
        {
            return !string.IsNullOrEmpty(inputName) && inputSalary > 0;
        }
    }
    public interface IStorageAgent
    {
        IDbConnection GetPersistantStorageConnection();
        void SaveDataToStorage(IDbConnection Connection, string queryText);
        
    }
    public class DatabaseStorageAgent : IStorageAgent
    {
        public IDbConnection GetPersistantStorageConnection()
        {
            return new SqlConnection("Data Source=(local);
            Initial Catalog=EmployeeDB;Integrated Security=SSPI;");
        }

        public void SaveDataToStorage(IDbConnection dbConnection, string queryText)
        {
            var sqlCmd = new SqlCommand(queryText, (SqlConnection)dbConnection);
            dbConnection.Open();
            sqlCmd.ExecuteNonQuery();
        }
    }
}

I'm trying to evolve with concepts in the same way like I did in my previous article. So are you able to notice the evolution in "Employee" class. Yes. I've chosen constructor parameters as my point of injection. I removed all the object instantiation code from "Employee" class as I don't want to take up that responsibility. I expect those dependency objects to be passed into my object instance through constructor. Also just for your information, this is NOT the ONLY way to inject your dependencies into your class. Can you think of other possible points of injection? The current way we just learned is called Constructor Dependency Injection. Congratulations! You just learned a new technical concept to impress your colleagues.

But there is more to learn here.

Inversion of Control Principle

Now, you should observe one important thing here. To make your dependencies pluggable at run time, you've inverted the control of creation and management of your dependency objects. The responsibility of creation of dependencies has moved away from your component or in a sense has got inverted towards main method (the calling component here). This is essentially the core principle Inversion of Control (IoC) on which dependency injection pattern is based upon. So basically, we understood the two concepts in bottoms up approach. We first learned Dependency Injection pattern and then the higher level Inversion of Control design principle on which it is based upon. I hope you are able to appreciate it.

But, is this enough to ensure pluggability feature of your dependencies? No. Read on.

IoC/DI Containers

You can still say that though I inverted the control of creation of object from "Employee" class to main method by injecting dependencies at run time, but main method is still a part of your program only. Isn't it? So if my dependencies change, then main method also changes which is essentially a change in my own program. I want to get rid of it completely. To enable you to accomplish this goal, a separate component is generally used. That piece of software is essentially a factory whose sole responsibility is to create/manage objects or instances of your dependencies. That piece of software/component is known as IoC/DI container. Here is how IoC/DI containers work:

  1. You configure a DI container. This means you tell the DI container that if I ask you for reference of an interface/class, then which class should be instantiated, i.e., you register the mappings of class names of dependencies with corresponding interface/class name. For e.g., In my current case when I need reference of IStorageAgent, the DI container should create an instance of DatabaseStorageAgent class and return me back the reference. I've used Windsor Castle DI container in my attached sample code. Have a look at RegisterDependencyMapping function in DIContainer.cs file in code snippet # 3 below. It tells you how the current DI container in use is configured.
  2. You call the DI container to resolve the dependency from main method at run time. This essentially means you are calling the DI container to get the desired dependency object instance based on the configuration set in step 1 above.

Let's look at code and see how Main.cs changes:

Code Snippet # 3

Main.cs inside ClientAppWithDIContainer.csproj:

using System;
using Castle.Windsor;
using DependencyInjection;
using DIContainer;
namespace DependencyInjectionWithDIContainer
{
    class Program
    {
        static void Main(string[] args)
        {
            //Get DI container reference
            IWindsorContainer windsorContainer = WindsorCastleDIContainer.Container;
            //Initialize windsor castle DI container dependency mappings
            WindsorCastleDIContainer.RegisterDependencyMapping(windsorContainer);
            //Get dependency instance reference using DI container
            //This is also called Resolving a dependency using DI container.
            IStorageAgent sqlServerPersistanceStorageAgent = windsorContainer.Resolve<IStorageAgent>();
            //inject dependency through constructor injection
            var employee = new Employee(sqlServerPersistanceStorageAgent);
            employee.EmployeeName = "Rasik";
            employee.Salary = 200;
            employee.SaveEmployeeDetails();
            Console.WriteLine("Dependency Injection accomplished 
            with the help of Windsor Castle DI container. Press enter to end the program.");
            Console.ReadLine();
        }
    }
}

DIContainer.cs inside DIContainer.csproj:

using Castle.MicroKernel.Registration;
using Castle.Windsor;
using DependencyInjection;

namespace DIContainer
{
    public class WindsorCastleDIContainer
    {
        private static WindsorContainer container = new WindsorContainer();

        public static IWindsorContainer Container
        {
            get
            {
                return container;
            }
        }

        public static void DisposeDIContainer()
        {
            if (container != null)
            {
                container.Dispose();
                container = null;
            }
        }

        public static void RegisterDependencyMapping(IWindsorContainer container)
        {
            container.Register(Component.For<IStorageAgent>().ImplementedBy<DatabaseStorageAgent>());
        }
    }
}

Your core component containing employee class remains absolutely unchanged. Now what advantages do I get when this DI container project (which is the factory for creation of my dependency objects) has come into the picture:

  1. The DI container is in a separate project/assembly. It can be changed, compiled and deployed independently without affecting your other core components.
  2. Whenever a dependency mapping changes ONLY my DI container component gets affected. Make changes in RegisterDependencyMapping method and you are all good. For example, as in my case, my customer wants to switch to Microsoft Access based persistent storage implementation. I already have that implementation ready as "MSAccessStorageAgent" class. So just replace "DatabaseStorageAgent" class name with "MSAccessStorageAgent" in RegisterDependencyMapping function and you are done.
  3. You don't have to bother about lifetime and management of objects of your dependency. DI containers are smart enough to take care of this.

DI containers can be configured to return singleton or a new instance of your dependency object on a per call basis. You should explore more on this as DI containers are a world in their own and I can't accommodate more details on them in this article. There are numerous DI containers available in the market. For .NET environment, you can get an exhaustive list here.

What if Dependency Inversion Principle is Missing

The current shape of employee is exhibiting two key features:

  1. It follows dependency inversion principle. We learned about this in part I of this article.
  2. It implements dependency injection design pattern to enable inversion of control of creation of its dependencies. We learned about this in the current article.

What if I let go of feature # 1 and continue only with feature # 2. The impact is simple, every time you have to substitute/update your dependency, you make hell lot of changes - The employee component, the main method and the DI container configuration of course. I've also attached an employee class which conforms to feature # 2 above but not feature #1. I'm sure you always want least changes in your software for any new customer requirement. The choice is yours. DIP and IoC create best impact when used together. Here is the code snippet which has dependency injection (IoC principle) in place but is not following DIP (Dependency inversion principle). Compare it with EmployeeComponentWithConstructorDI.cs file and see how the injection parameters in constructor of "Employee" class has changed from IStorageAgent to DatabaseStorageAgent. This violation of DIP has a costly impact.

Code Snippet # 4

EmployeeComponentWithoutDIP.cs

using System.Data;
using System.Data.SqlClient;
using System.Data.OleDb;

namespace DependencyInjectionWihtoutDIP
{
    public class Employee
    {
        private string empName;
        private int salary;
        DatabaseStorageAgent persistanceStorageDependency;

        public Employee(DatabaseStorageAgent persistanceStorageAgent)
        {
            //Assigning the injected dependency to local reference.
            persistanceStorageDependency = persistanceStorageAgent;
        }

        public string EmployeeName
        {
            get { return empName; }
            set { empName = value; }
        }
        public int Salary
        {
            get { return salary; }
            set { salary = value; }
        }
        public void SaveEmployeeDetails()
        {
            if (IsValidEmployee(empName,salary))
            {
                //create query text
                string queryText = string.Format
                ("insert into Employee (Name,Salary) values ('{0}',{1})", empName, salary);

                //create persistence storage connection
                var connection = persistanceStorageDependency.GetPersistantStorageConnection();

                //save employee details into the persistence storage
                persistanceStorageDependency.SaveDataToStorage(connection, queryText);
            }
        }
        private bool IsValidEmployee(string inputName, int inputSalary)
        {
            return !string.IsNullOrEmpty(inputName) && inputSalary > 0;
        }
    }

    public interface IStorageAgent
    {
        IDbConnection GetPersistantStorageConnection();
        void SaveDataToStorage(IDbConnection Connection, string queryText);
        
    }

    public class DatabaseStorageAgent : IStorageAgent
    {
        public IDbConnection GetPersistantStorageConnection()
        {
            return new SqlConnection("Data Source=(local);
            Initial Catalog=EmployeeDB;Integrated Security=SSPI;");
        }

        public void SaveDataToStorage(IDbConnection dbConnection, string queryText)
        {
            var sqlCmd = new SqlCommand(queryText, (SqlConnection)dbConnection);
            dbConnection.Open();
            sqlCmd.ExecuteNonQuery();
        }
    }

    public class MSAccessStorageAgent : IStorageAgent
    {
        public IDbConnection GetPersistantStorageConnection()
        {
            return new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;
            Data Source=C:\\AccessDatabases\\EmployeeDb\\employee.mdb;Persist Security Info=True");
        }

        public void SaveDataToStorage(IDbConnection dbConnection, string queryText)
        {
            var oleDbCmd = new OleDbCommand(queryText, (OleDbConnection)dbConnection);
            oleDbCmd.ExecuteNonQuery();
        }
    }
}

What Advantages You Get Through Dependency Injection

Apart from minimizing your changes due to your changing requirements, one other core benefit of Dependency injection pattern is unit testability of your components through mocking, i.e., at run time instead of injecting the actual/real dependency classes which represent external resources you can inject fake/dummy components who can act just like your real dependency class without connecting to external resources. This helps you in limiting the unit testing boundary to the logic built in your component ONLY.

Points of Interest

  • Explore other ways of dependency injection.
  • Explore other scenarios for Inversion of Control. Currently, you inverted the control of instantiation of your dependencies.
  • Configuration of IoC containers through configuration files rather than code.
  • What are Service locators. They are siblings of IoC containers and follow service location design pattern.
  • How to do mocking of dependency components for unit testing.
  • Mocking frameworks.

History

  • Updated to incorporate reader's feedback

License

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

Share

About the Author

Rasik Bihari Tiwari
Software Developer 1E
India India
My professional details will hardly make a difference to you if you have come to know about me after reading my lengthy article but my intent will make a difference for sure. Why I write articles is for the sole motto which I got to know pretty late in my career and that is that there is ONLY and ONLY one way of enhancing knowledge and that way is sharing. Blogs, articles and white papers are nothing but a form of sharing. It also helps you creating a backup copy of the knowledge you have in your brain and nerves. This backup copy will surely outlive your life as this knowledge gets transferred to uncountable people through sharing. So start contributing today and always remember : "Knowledge is the ONLY antidote to all fears". Start reading a new book today. Don't seek Nirvana. Seek knowledge. Nirvana and knowledge are synonymous.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionHow to use unity in your code instead of windsor Pin
Tridip Bhattacharjee9-Feb-17 21:33
professionalTridip Bhattacharjee9-Feb-17 21:33 
Suggestiona tiny idea Pin
TolgaEvcimen9-Feb-17 19:56
professionalTolgaEvcimen9-Feb-17 19:56 
GeneralRe: a tiny idea Pin
Rasik Bihari Tiwari10-Feb-17 0:33
professionalRasik Bihari Tiwari10-Feb-17 0:33 
GeneralMy vote of 5 Pin
Vaso Elias1-Feb-16 8:23
memberVaso Elias1-Feb-16 8:23 
PraiseNice job Pin
ahagel27-Jan-16 7:33
memberahagel27-Jan-16 7:33 
QuestionVery good explanation, but not quite at your 100% target Pin
jsc4224-Jan-16 22:10
professionaljsc4224-Jan-16 22:10 
PraiseRe: Very good explanation, but not quite at your 100% target Pin
Rasik Bihari Tiwari26-Jan-16 13:31
memberRasik Bihari Tiwari26-Jan-16 13:31 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.171020.1 | Last Updated 26 Jan 2016
Article Copyright 2016 by Rasik Bihari Tiwari
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid