
How does the built-in dependency injection work on ASP.NET Core?
Introduction:
Dependency Injection (DI) is a pattern that can help developers decouple the different pieces of their applications. DI provides a mechanism for the construction of dependency graphs independent of the class definitions.
NET Core supports the DI software design pattern, which is a technique for achieving Inversion of Control (IoC, the “D” on the SOLID principles) between classes and their dependencies.
The Problem:
The Dependency Inversion Principle states that:
- High-level modules should not depend on low-level modules. Both should depend on abstractions
- Abstractions should not depend on details. Details should depend on abstractions
To better understand the above statements, you should probably ask yourself:
- Have you ever had to change a lot of code because of a new simple requirement?
- Have you ever had a hard time trying to refactor a part of your application?
- Have you ever been in trouble writing unit tests because of components that required other components?
If you answered yes to any of these questions, maybe your codebase suffers from dependency. It’s a typical disease of the code of an application when its components are too coupled. In other words when a component depends on another one in a too-tight way. The main effect of component dependency is the maintenance difficulty of the code, which, of course, implies a higher cost.
Take a look at the following code:
In this example, Foo depends on IBar and somewhere you’ll have to construct an instance of Foo and specify that it depends on the implementation Bar like so:
There are a couple of problems with this. Firstly, it violates the Dependency Inversion Principle because the consuming class implicitly depends on the concrete types Bar and Foo. Secondly, it results in a scattered definition of the dependency graph and can make unit testing very difficult (because Foo and Bar can’t be mocked).
The Composition Root pattern states that the entire dependency graph should be composed in a single location “as close as possible to the application’s entry point” (your Startup class on ASP.NET Core and your Program class on .Net Core).
Our Solution:
The .NET Core built-in DI provides an IoC mechanism, often referred to as a “Container”, for offloading the instantiate, injection, and lifetime management of the application’s dependencies. You invert the control of component instantiate from the consumers to the container, hence “Inversion of Control”.
To do this, you simply register services within the container, and then you can load the top-level service. The framework will inject all child services for you.
Key Concepts:
- Dependency Inversion Principle: It’s a software design principle. It suggests a solution to the dependency problem but does not say how to implement it or which technique to use
- Inversion of Control (IoC): This is a way to apply the Dependency Inversion Principle. IoC is the actual mechanism that allows your higher-level components to depend on abstraction rather than the concrete implementation of lower-level components. IoC is also known as the Hollywood Principle. This name comes from the Hollywood cinema industry, where, after an audition for an actor role, usually the director says, don’t call us, we’ll call you
- Dependency Injection: This is a design pattern to implement the IoC. It allows you to inject the concrete implementation of a low-level component into a high-level component
- IoC Container: Also known as DI Container, it’s a programming framework that provides you with an automatic DI of your components
Dependency Injection approaches:
- Constructor Injection: One of the most popular DI technics. With this approach, you create an instance of your dependency and pass it as an argument to the constructor of the dependent class
- Method Injection: In this case, you create an instance of your dependency and pass it to a specific method of the dependent class
- Property Injection: This approach allows you to assign the instance of your dependency to a specific property of the dependent class
Service Lifetimes:
Sometimes you might need a single instance of our dependency that will live for the entire lifetime of your application. This may be suitable for a service like a logger or a helper, but it’s unacceptable for other services. The IoC Container allows you to control the lifetime of a registered service. When you register a service specifying a lifetime, the container will automatically dispose of it accordingly. There are three service lifetimes:
- Singleton: This lifetime creates one instance of the service. The service instance may be created at the registration time by using the Add()/AddSingleton()method
- Transient: By using this lifetime, your service will be created each time it will be requested. This means, for example, that a service injected in the constructor of a class will last as long as that class instance exists. To create a service with the transient lifetime, you have to use the AddTransient()method
- Scoped: The scoped lifetime allows you to create an instance of a service for each client request. This is particularly useful in the ASP.NET context since it allows you to share the same service instance for the duration of an HTTP request processing. To enable the scoped lifetime, you need to use the AddScoped()method
Written by:
Email ID: [email protected]