ASP.NET Core supports the Dependency Injection (DI) to achieve Inversion of Control. It has a built-in DI container for internal services. Dependency Injection helps you to develop decoupled software application through constructor or property injection. The main idea behind DI is that a class should depend on abstractions rather than concrete implementations of classes.
Define What Is Dependency Injection
In real word, dependency means to rely on other things for the support. Before moving towards dependency injection, you must know what dependency in the programming is. If class A is using some functionality of the class B, which means you can say that the class A has a dependency on a class B to complete its functionality. Dependency Injection helps you to produce a loosely coupled code, which means that the two objects must be independent and both can use each other without having any dependency on each other. The DI should handle if there is any change in the class without affecting the other objects. The software should not have any static or hard-coded configurations, but it should be done at run-time. It is a design pattern makes loosely coupled software components and reduces the risk of changing inter-connected dependencies if there is a change in just one object.
Provide An Overview Of How Dependency Injection Works
Dependency Injection is a technique to remove the dependencies of one class on another by making it independent and decouple the usage of shared objects. It uses a builder object to initialize the objects and injects the dependency from outside the class by providing the required dependencies.
Dependency Injection has the following responsibilities:
- Create the objects of classes
- Keep a track of classes that have dependency on other classes
- Provide all the objects that are required by the classes to complete the functionality
What Are The Different Ways To Implement Dependency Injection.
Dependency Injection is a design pattern that allows you to implement IoC. Let us take an example of Account class, which has references to the Current Account and Saving Account.
public class SavingAccount
{
public void GetData()
{
Console.WriteLine("Get Saving Account Data.");
}
}
public class CurrentAccount
{
public void GetData()
{
Console.WriteLine("Get Current Account Data.");
}
}
//CustomerAccount is tightly dependent on Saving and Current Account classes
public class CustomerAccount
{
// Create object of the class SavingAccount
SavingAccount objSavingAccount = new SavingAccount();
// Create object of the class CurrentAccount
CurrentAccount objCurrentAccount = new CurrentAccount();
public void GetAccountData()
{
objSavingAccount.GetData();
objCurrentAccount.GetData();
}
}
public class Program
{
static void Main(string[] args)
{
CustomerAccount objCustomerAccount = new CustomerAccount();
account.GetAccountData();
Console.ReadLine();
}
}
Constructor Injection
The Constructor Injection uses a parameter to inject dependencies in a class. This is the most common method of Dependency Pattern; therefore, there is one parameterized constructor but no default constructor and you need to pass the specified values to the constructor.
//Create ICustomerAccount interface to decouple the objects
public interface ICustomerAccount
{
void GetData ();
}
//Implemented the IAccount interface in SavingAccount class
public class SavingAccount : ICustomerAccount
{
public void GetData()
{
Console.WriteLine("Get Saving Account Data.");
}
}
public class CurrentAccount : ICustomerAccount
{
public void GetData()
{
Console.WriteLine("Get Current Account Data.");
}
}
public class CustomerAccount
{
private ICustomerAccount CustomerAccount;
//Passing ICustomerAccount interface as a parameter
public Account(ICustomerAccount account)
{
this.CustomerAccount = account;
}
public void GetAccountData()
{
CustomerAccount.GetData();
}
}
class Program
{
static void Main()
{
//Create an object of SavingAccount() and pass it to CustomerAccount constructor
ICustomerAccount savingAccount = new SavingAccount();
CustomerAccount account = new CustomerAccount (savingAccount);
account.GetAccountData();
//Create an object of CurrentAccount() and pass it to CustomerAccount constructor
ICustomerAccount currentAccount = new CurrentAccount();
account = new CustomerAccount(currentAccount);
account.GetAccountData();
Console.ReadLine();
}
}
Setter Injection
The Setter Injection allows you to remove the dependencies through the properties of a class
public class CustomerAccount
{
public ICustomerAccount CustomerAccount {get; set;}
public void GetAccountData()
{
CustomerAccount.GetData();
}
}
class Program
{
static void Main()
{
//Create an object of SavingAccount() and assign it to the property
CustomerAccount account = new CustomerAccount ();
account.CustomerAccount = new SavingAccount();
account.GetAccountData();
//Create an object of CurrentAccount() and assign it to the property
CustomerAccount account = new CustomerAccount ();
account.CustomerAccount = new CurrentAccount();
account.GetAccountData();
Console.ReadLine();
}
}
Interface-based injection
In Interface-based Injection, the injection is made directly into class through a method.
//Create ICustomerAccount interface to decouple the objects
public interface ICustomerAccount
{
void GetData ();
}
//Implemented the IAccount interface in SavingAccount class
public class SavingAccount : ICustomerAccount
{
public void GetData()
{
Console.WriteLine("Get Saving Account Data.");
}
}
public class CurrentAccount : ICustomerAccount
{
public void GetData()
{
Console.WriteLine("Get Current Account Data.");
}
}
public class CustomerAccount
{
public void GetAccountData(ICustomerAccount account)
{
account.GetData();
}
}
class Program
{
static void Main()
{
//Create an object of SavingAccount() and pass it to the method
CustomerAccount account = new CustomerAccount ();
account.GetAccountData(new SavingAccount());
//Create an object of CurrentAccount() and pass it to the method
CustomerAccount account = new CustomerAccount ();
account.GetAccountData(new CurrentAccount());
Console.ReadLine();
}
}
Service Locator Injection
Service Locator is a different injection pattern, which can return strong or generic type object. It helps you to locate the service. It suggests not creating an object of one class into another.
public interface IEmailService
{
void ExecuteEmailService();
}
public class EmailService : IEmailService
{
public void ExecuteEmailService ()
{
Console.WriteLine("Executing Email Service");
}
}
public static class ServiceLocator
{
public static IEmailService objService = null;
//Service locator function returning strong type
public static IEmailService SetLocation(IEmailService tmpser)
{
if (objService == null) return new EmailService ();
return objService;
}
//Execute service
public static void ExecuteEmailService ()
{
objService.ExecuteEmailService();
}
}
class Program
{
static void Main(string[] args)
{
IEmailService svr = EmailServiceLocator.SetLocation(new EmailService());
svr.ExecuteEmailService();
Console.ReadLine();
}
}
What Are The Advantages Of Dependency Injection
Dependency Injection has several benefits; some of them are listed below:
- Maintainability
It helps in maintaining the code structure of a program. It is relatively easier to follow single principle.
- Flexibility
It helps you in achieving the loosely coupled code, which makes it flexible to use in different ways.
- Testability
It is easy to do the unit testing with different mock implementations when there are no tightly coupled objects.
- Readability
The classes are well defined by having logic in constructors, openly declaring what is written in it.
What Are The Disadvantages Of Dependency Injection
- Complexity
The Dependency Injection follows the Single Responsilibity Principle, the complexity of the program increases as it has more classes.
- Runtime Errors
The Dependency Injection can have circular dependencies, bad bindings, etc. if the configurations are not done correctly. Such errors will not be catch at compile time.
- DI Framework
If you are using any DI Framework to handle the code, your code is limited to the framework.
Difference Between Dependency Injection And Factory Pattern
Factory Patterns are responsible for creating the objects or instances of classes, whereas Dependency Injection is more likely to be concerned with the loose coupling and Inversion of Control.
Factory Pattern is like a tool to implement Dependency Injection, as it is a way to separate the responsibility of creating instances of classes, whereas DI is more like an architectural pattern for loosely coupled software programming.