Using Dependency Injection in Asp.Net Core
What is Dependency Injection?
The dependency injection (DI) is a software
design pattern, which is a technique for achieving Inversion of control (IOC)
between classes and their dependencies.
ASP.NET Core is designed from scratch to
support Dependency Injection. ASP.NET Core injects objects of dependency
classes through constructor or method by using built-in IoC container.
Why use Dependency Injection?
Using Dependency Injection, we make a class
independent of its dependencies. It achieves that by decoupling the usage of an
object from its creation. In other terms, the intent of Dependency Injection is
to make code maintainable by allowing us to develop loosely-coupled code.
Technically saying, DI reduces the hard-coded dependencies among the classes by
injecting those dependencies at run time instead of design time.
Overview of Dependency Injection
A dependency is any object, that another
object requires. Dependency Injection allows the creation of dependent objects
outside of a class and provides those objects to a class in different
ways. Using DI, we move the creation and binding of the dependent objects
outside of the class that depends on them.
The Dependency Injection pattern involves 3
types of classes which are
Client Class: The client class (dependent
class) is a class which depends on the service class
Service Class: The service class
(dependency) is a class that provides service to the client class.
Injector Class: The injector class injects
the service class object into the client class.
As you can see, the injector class creates
an object of the service class and injects that object to a client object. In
this way, the DI pattern separates the responsibility of creating an object of
the service class out of the client class.
An instance of the MyServiceClass class can
be created to make the WriteMessage method available to a class. The
MyServiceClass class is a dependency of the MyClientClass class.
The class creates and directly depends on
the MyServiceClass instance. Code dependencies (such as the previous example)
are problematic and should be avoided for the following reasons:
- To replace MyServiceClass with
a different implementation, the class must be modified.
- If MyServiceClass has
dependencies, they must be configured by the class. In a large project with
multiple classes depending on MyServiceClass, the configuration code becomes
scattered across the app.
- This implementation is
difficult to unit test. The app should use a mock or stub MyServiceClass class,
which isn't possible with this approach.
Dependency injection addresses these
problems through:
- The use of an interface or base
class to abstract the dependency implementation.
- Registration of the dependency
in a service container. ASP.NET Core provides a built-in service container,
IServiceProvider. Services are registered in the app's
Startup.ConfigureServices method.
- Injection of the service into
the constructor of the class where it's used. The framework takes on the
responsibility of creating an instance of the dependency and disposing of it
when it's no longer needed.
Let's use Dependency Injection in application
Consider the following example of a simple
ILog interface and its implementation class. We will see how to register it
with built-in IoC container and use it in our application.
public interface ILog
{
void info(string str);
}
class MyConsoleLogger : ILog
{
public void info(string str)
{
Console.WriteLine(str);
}
}
ASP.NET Core allows us to register our
application services with IoC container, in the ConfigureServices method of the
Startup class. The ConfigureServices method includes a parameter of
IServiceCollection type which is used to register application services.
Let's register above ILog with IoC
container in ConfigureServices() method, which can be done in 3 ways as shown
below:
publicclass
Startup
{
public
void
ConfigureServices(
IServiceCollectionservices)
{
// Singleton:- IoC container will create and sare a single instance of a service
throughout the application’s lifetime.
services.Add(
newServiceDescriptor
(
typeof(
ILog),
newMyConsoleLogger
()));
// Transient:- IoC container will create a new instance of the specified service
type every time you ask for it.
services.Add(
newServiceDescriptor
(
typeof(
ILog),
typeof(
MyConsoleLogger),
ServiceLifetime
.Transient));
// Scoped:- IoC container will create an instance of the specified service
type oce per request and will be shared in a single request.
services.Add(
newServiceDescriptor
(
typeof(
ILog),
typeof(
MyConsoleLogger),
ServiceLifetime
.Scoped));
}
}
Types of Dependency Injection
The injector class injects dependencies
broadly in three ways:
- Constructor Injection
- Method Injection
- Property Injection
Constructor Injection
In the constructor injection, the injector
supplies the service (dependency) through the client class constructor.
Once we register a service, the IoC
container automatically performs constructor injection if a service type is
included as a parameter in a constructor.
For example, we can use ILog service type
in any MVC controller. Consider the following example:
publicclass
HomeController
:
Controller
{
ILog
_log;
public
HomeController(
ILoglog)
{
_log = log;
}
public
IActionResult
Index()
{
_log.info(
"Executing /home/index");
return
View();
}
}
In the above example, an IoC container will
automatically pass an instance of MyConsoleLogger to the constructor of
HomeController. We don't need to do anything else. An IoC container will create
and dispose an instance of ILog based on the registered lifetime.
Method Injection
In this type of injection, the client class
implements an interface which declares the method(s) to supply the dependency
and the injector uses this interface to supply the dependency to the client
class. For this, use [FromServices] attribute with the service type parameter
in the method:
publicclass
HomeController
:
Controller
{
public
HomeController()
{
}
public
IActionResult
Index([
FromServices]
ILoglog)
{
log.info(
"Index method executing");
return
View();
}
}
Property Injection
Built-in IoC container does not support
property injection. You will have to use third party IoC container. For this,
use [FromServices] attribute with the service type parameter in the method.
Conclusion
Dependency Injection (DI) describes the
pattern of passing dependencies to consuming services at instantiation. DI
frameworks provide IoC containers that allow developers to offload control of
this process to the framework. This lets us decouple our modules from their
concrete dependencies, improving testability and extensibility of our
applications.
Comments
Post a Comment