DI is a technique in software development that manages the dependencies between different components or modules in a system.
This is done by providing a component with its required dependencies from an external source instead of allowing it to create or manage its dependencies .
DI achieves inversion of control (IOC) between classes and their dependencies.
Inversion of control (IOC)
its a design pattern that allows the flow of control in a program to be inverted or reversed. Instead of a program controlling the flow of execution, the flow is controlled by an external framework or container.
Why IoC ?
By applying IoC, an application can decouple its components, making them more modular and easier to test and maintain.
IoC promotes the use of interfaces and abstraction, which makes the code more flexible and extensible.
Types of DI
Constructor Injection: This involves injecting dependencies through a class’s constructor. This is the most common form of DI and is often used when a class has mandatory dependencies that it needs in order to function correctly.
Property Injection: This involves injecting dependencies through a class public properties. Property injection is less common than constructor injection, and is often used when you need to inject optional dependencies.
Method Injection: This involves injecting dependencies through a method call. Method Injection is the least common form of DI, and is often used when a dependency is only required for a specific method call.
DI in .NET Core
DI in .NET Core is a built-in feature that allows you to separate the creation of object graphs from your application code.
To use DI, you need to register your services with a service container by specifying which services your application needs and how to create them.
Lifetime Management
Lifetime Management in Dependency Injection refers to the way in which instances of a service are created and managed by the Dependency Injection container. In other words, it determines the lifespan of an object created through Dependency Injection.
The Three Lifetime Management Options:
Transient
This is the default lifetime management in .NET Core. A new instance of the service is created each time it is requested from the container
Methods:
AddTransient:
AddTransient<TService, TImplementation>
Registers a service and its implementation with the transient lifetime management.
When you have a lightweight and stateless service that does not maintain any state between requests, such as a logger or a helper class.
When you want to ensure that a new instance of the service is created every time it is requested from the container. This can help prevent issues with thread-safety or shared state.
Scoped
A single instance of the service is created for each scope. A scope typically corresponds to a web request in web application.
Methods
AddScoped:
AddScoped<TService, TImplementation>
Registers a service and its implementation with the scoped lifetime management.
When you have a service that needs to maintain state between requests, but that should not be shared across multiple requests, such as a shopping cart in an e-commerce application.
When you want to optimize performance by reusing a single instance of the service across all objects that are created within a particular scope, such as a web requests in a web application.
Singleton
A single instance of the service is created and shared throughout the lifetime of the application. This can be useful for services that are expensive to create or for services that need to maintain state.
Methods
AddSingleton:
AddSingleton<TService, TImplementation>
Registers a service and its implementation with the singleton lifetime management.
Be aware that a single instance of the service will be created and shared throughout the lifetime of the application. This can lead to issues with shared state or thread-safety if the service is not designed to be used in this way.
Be sure to handle any concurrency issues that may arise when using a singleton service. This can include locking mechanisms or other techniques to prevent multiple threads from accessing the service at the same time.
Example:
public interface IMyDependency
{
void DoSomething();
}
public class MyDependency : IMyDependency
{
public void DoSomething()
{
Console.WriteLine("Doing something...");
}
}
Configure Dependency Injection Container
In your application’s startup code, typically in the ConfigureServices
method of the Startup
class, configure the dependency injection container to use the desired implementation for the interface.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMyDependency, MyDependency>();
// Other service configurations...
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IMyDependency, MyDependency>();
// Other service configurations...
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMyDependency, MyDependency>();
// Other service configurations...
}
Use the Dependency
In your application classes or controllers, you can inject the dependency using constructor injection.
public class MyService
{
private readonly IMyDependency _myDependency;
public MyService(IMyDependency myDependency)
{
_myDependency = myDependency;
}
public void DoWork()
{
_myDependency.DoSomething();
}
}