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:
- 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.
- Understand that Dependency Injection can be implemented even if your modules and components don't follow or exhibit the Dependency Inversion principle.
- 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:
- Visual Studio 2010 or above
- 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
{
public void SaveEmployeeDetails()
{
IStorageService persistanceStorageAgent = new DatabaseStorageAgentForSqlServer();
if (IsValidEmployee(empName,salary))
{
persistanceStorageAgent.SaveData(empName, salary);
}
}
}
public interface IStorageService
{
void SaveData(string name, int salary);
}
public class DatabaseStorageAgentForSqlServer : IStorageService
{
public void SaveData(string name, int salary)
{
string queryText = string.Format
("insert into Employee (Name,Salary) values ('{0}',{1})", name, salary);
var oleDbCon = GetPersistantStorageConnection();
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:
- 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.
- 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. - 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. - 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)
{
IStorageAgent sqlServerPersistanceStorageAgent = new DatabaseStorageAgent();
var employee = new Employee(sqlServerPersistanceStorageAgent);
employee.EmployeeName = "Rasik";
employee.Salary = 200;
employee.SaveEmployeeDetails();
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)
{
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))
{
string queryText = string.Format
("insert into Employee (Name,Salary) values ('{0}',{1})", empName, salary);
var connection = persistanceStorageDependency.GetPersistantStorageConnection();
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:
- 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. - 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)
{
IWindsorContainer windsorContainer = WindsorCastleDIContainer.Container;
WindsorCastleDIContainer.RegisterDependencyMapping(windsorContainer);
IStorageAgent sqlServerPersistanceStorageAgent = windsorContainer.Resolve<IStorageAgent>();
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:
- The DI container is in a separate project/assembly. It can be changed, compiled and deployed independently without affecting your other core components.
- 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. - 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:
- It follows dependency inversion principle. We learned about this in part I of this article.
- 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)
{
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))
{
string queryText = string.Format
("insert into Employee (Name,Salary) values ('{0}',{1})", empName, salary);
var connection = persistanceStorageDependency.GetPersistantStorageConnection();
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