Introduction
In this article we will try to learn the concept of "Dependency Injection" with a very simple example. but before going to that example let me give an basic introduction to the topic.In these day's application need to have such an software development design which can easily cater frequently coming new changes and updates. for that purpose you need to develop a loosely coupled design/architecture for your application. To develop a loosely couple design for an application you can use dependency injection.
There are interrelated concepts "Inversion of Control (IoC)" and "Dependency Injection (DI)". Inversion of Control (IoC) is a prerequisite for Dependency Injection (DI). Dependency Injection is a way to implement inversion of control.
Inversion of Control (IoC):
This is a more generic concept which allows the framework to call the implementations provided by the application instead of the application calls the method of framework.Dependency Injection (DI):
Dependency Injection is a concrete term, and it is a technique which helps to inject dependent objects of a class at run time by using different injection techniques like Setter Injection, Constructor Injection or by Interface Injection.Now to go through it, I have taken a simple example of 3 layer architecture application. This just a sample application and I have kept it very simple to understand the actual concept. By using this application I would briefly discuss the 3 layer architecture and it's problems. when we talk about 3 layer architecture its as follow [User Interface(UI) >> Business Logic(BL) >> Data Access Layer(DAL) ]

I have created a very simple console application (c#) in which there is a Program class as (UI), Person class as (BL) and SQLServerDAL class as (DAL).
In this application my (UI) layer communicates with business layer which is person class and person class internally calls the (DAL). In a real world application you would add separate class library for BL and DAL but to keep it simple I have just added them in the same project.
namespace DependencyInjection1
{
//UI
class Program
{
static void Main(string[] args)
{
Person obj = new Person();
//that generally should come from some text box.
obj.PersonName = "Tabish";
obj.add();
Console.ReadLine();
}
}
//middle Layer/business layer
public class Person
{
private SQLServerDAL ODalSQL = new SQLServerDAL();
public string PersonName { get; set; }
public void add()
{
ODalSQL.add();
}
}
//DAL
public class SQLServerDAL
{
public void add()
{
Console.Write("data Inserted through SQLServerDAL");
}
}
}
Now what is the problem with the above 3 layer architecture project? the answer is, it is tightly coupled. for example currently it is using SQL server DAL but what if your client demands to use Oracle data access layer instead. because it is a tightly coupled application so you needs to perform so many changes for this task, you will add a new "OracleDAL" then create its object in the person class and then call its add method depending upon the conditions as shown below.
namespace DependencyInjection2
{
//UI
class Program
{
static void Main(string[] args)
{
Person obj = new Person();
//that generally should come from some text box.
obj.PersonName = "Tabish";
obj.add();
Console.ReadLine();
}
}
//middle Layer/business layer public class Person
{
private SQLServerDAL ODalSQL = new SQLServerDAL();
private OracleDAL ODalOracle = new OracleDAL();
public string PersonName { get; set; }
public void add()
{
if (true)
{
ODalSQL.add();
}
else
{
ODalOracle.add();
}
}
}
//DAL
public class SQLServerDAL
{
public void add()
{
Console.Write("data Inserted through SQLServerDAL");
}
}
public class OracleDAL
{
public void add()
{
Console.Write("data Inserted through OracleDAL");
}
}
}
That is because it is not a very good architecture. A good architecture means when you make some changes in one module it should start reflecting in all modules. A good architecture should minimize the number of changes for example a new requirement comes for which you need to make changes to 15 to 20 places, a good architecture will cater this requirement with changes on 3 to 5 places only.
Now if you want to move from this tightly coupled architecture to a loosely coupled architecture you needs to add an interface. An generic interface which will points to one of the DAL either SQL or Oracle. Here I have added a interface IDAL and inherit both my "SQLServerDAL" and "OracleDAL" from that newly created interface (IDAL) which will now points to any one of the DAL by the virtue of polymorphism. In polymorphism we have the concept that same object can points to different object depending upon the conditions. as shown in the code below.
namespace DependencyInjection3
{
//UI
class Program
{
static void Main(string[] args)
{
Person obj = new Person();
//that generally should come from some text box.
obj.PersonName = "Tabish";
obj.add();
Console.ReadLine();
}
}
//middle Layer/business layer
public class Person
{
//private SQLServerDAL ODalSQL = new SQLServerDAL();
//private OracleDAL ODalOracle = new OracleDAL();
private IDAL Odal;
public string PersonName { get; set; }
public void add()
{
if (true)
{
Odal = new SQLServerDAL();
//ODalSQL.add();
}
else
{
Odal = new OracleDAL();
//ODalOracle.add();
}
Odal.add();
}
}
//DAL
public interface IDAL
{
void add();
}
public class SQLServerDAL : IDAL
{
public void add()
{
Console.Write("data Inserted through SQLServerDAL");
}
}
public class OracleDAL : IDAL
{
public void add()
{
Console.Write("data Inserted through OracleDAL");
}
}
}
After implementing the above code our code has reduced quite a lot. but here is another question is that code make my application loosely coupled ? the answer is again a big No. Because my code still referencing both my data access layers("SQLServerDAL" and "OracleDAL") in person class(BL), so that means if I have to add another data access layer let say "MySQLDAL" then again I have to reference my new object in the person class as well. while person class is my business logic layer, and it is not the responsibility of business logic layer to decide that which DAL will be used(new keyword shouldn't be there in BL) or which object of DAL to reference.
Now we are moving towards more loosely coupled architecture as in the following code we have created a constructor in Person class which takes "IDAL" object as a parameter and by using the following code we can now simply pass the object for required data access layer from our "UI" layer.
That means we have now given the control to our "UI" layer (Inversion of control) to decide which "DAL" to use.
namespace DependencyInjection4
{
//UI
class Program
{
static void Main(string[] args)
{
Person obj = new Person(new OracleDAL());
//that generally should come from some text box.
obj.PersonName = "Tabish";
obj.add();
Console.ReadLine();
}
}
//middle Layer
public class Person
{
private IDAL Odal;
public string PersonName { get; set; }
public Person(IDAL iobj)
{
Odal = iobj;
}
public void add()
{
Odal.add();
}
}
//DAL
public interface IDAL
{
void add();
}
public class SQLServerDAL : IDAL
{
public void add()
{
Console.Write("data Inserted through SQLServerDAL");
}
}
public class OracleDAL : IDAL
{
public void add()
{
Console.Write("data Inserted through OracleDAL");
}
}
}
Dependency Injection using unity container with configuration:
Moving one step ahead Applying dependency Injection using Microsoft unity we can install it through "NuGet Package Manager" as shown.Reference of our project will look like the following one.
Our final code will look like the following one. In which we have used a unity container which will behave like a basket which contains all the available objects. and a "LoadConfigration()" method which will load the list of available objects to the container. And we have also added a new data access layer named "MySQLDAL" as per our new requirement.
namespace DependencyInjection5
{ //UI
class Program
{ static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
container.LoadConfiguration();
IDAL dataAccess = container.Resolve<IDAL>("DataAccessLayer1");
//Register the instance
container = container.RegisterInstance<IDAL>(dataAccess);
Person obj = container.Resolve<Person>();
//that generally should come from some text box.
obj.PersonName = "Tabish";
obj.add();
Console.ReadLine();
}
}
//middle Layer
public class Person
{
private IDAL Odal;
public string PersonName { get; set; }
public Person(IDAL iobj)
{
Odal = iobj;
}
public void add()
{
Odal.add();
}
} //DAL
public interface IDAL
{
void add();
}
public class SQLServerDAL : IDAL
{
public void add()
{
Console.Write("data Inserted through SQLServerDAL");
}
}
public class OracleDAL : IDAL
{
public void add()
{
Console.Write("data Inserted through OracleDAL");
}
}
public class MySQLDAL : IDAL
{
public void add()
{
Console.Write("data Inserted through MySQLDAL");
}
}
{
public void add()
{
Console.Write("data Inserted through MySQLDAL");
}
}
}
we also need to add following configuration in the "App.config" file.(currently for console application for web application you can use web.config file) which will register all the available objects. as you can see in the following configuration we have registered all 3 objects of "IDAL" type.
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="IDAL" type="DependencyInjection5.IDAL, DependencyInjection5" />
<namespace name="DependencyInjection5" />
<assembly name="DependencyInjection5" />
<container>
<register type="IDAL" name="DataAccessLayer1" mapTo="SQLServerDAL">
</register>
<register type="IDAL" name="DataAccessLayer2" mapTo="MySQLDAL">
</register>
<register type="IDAL" name="DataAccessLayer3" mapTo="OracleDAL">
</register>
</container>
</unity>
</configuration>
If run the above code it will give following result.
It shows that currently data inserted using "SQLServerDAL" data access layer. now if want to use newly added "MySQLDAL" data access layer we just need to change one line as shown.
IDAL dataAccess = container.Resolve<IDAL>("DataAccessLayer2");
And we will get the desired result. and even that line contains the string name that we used while registering the object to the container in the configuration file. so that string can also comes from configuration as well. In that case if we want to add a new data access layer we need not change a single line of code for implementing the new data access layer. In case of such change request we just need to write the data access layer and we can use just through configuration setting.
Now our goal of decoupled architecture achieved.
very nice article (y)
ReplyDeletenice article
ReplyDeletehow to implement lazy loading for above code
ReplyDeleteI do not know how to implement dependency injection before. I have read several articles but i count not get proper understanding. Eventually i am able to understand the concept because of this article..Thank you so much
ReplyDelete