Introducing Dependency Injection in .NET
Dependency Injection
2 Articles
In this article, let's learn about Dependency Injection in .NET.
Table of Contents
- Introduction
- What is Dependency?
- Dependency Injection visualization
- Working with Dependency Injection
- Phases of Dependency Injection
- Advantages
- Summary
Introduction
.NET supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. Dependency injection in .NET is a built-in part of the framework.
What is Dependency?
A dependency is an objec that another object depends on. Examine the following ClassB class with that other classes depend on:
Code Sample - Dependency Injection Demo
A class can create an instance of the ClassB class to make use of it. In the following example, the ClassB class is a dependency of the ClassA class:
Code Sample - Dependency Injection Demo
The class creates and directly depends on the ClassB class. Hard-coded dependencies, such as in the previous example, are problematic and should be avoided for the following reasons:
- To replace ClassB with a different implementation, the ClassA class must be modified.
- If ClassB has dependencies, they must also be configured by the ClassA class. In a large project with multiple classes depending on ClassB, the configuration code becomes scattered across the app.
- This implementation is difficult to unit test. The app should use a mock or stub ClassB class, which isn't possible with this approach.
So let's try to get the instance of ClassB from constructor of ClassA.
Code Sample - Dependency Injection Demo
Here is the complete wire up in Program.cs.
Code Sample - Before Dependency Injection
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. .NET provides a built-in service container, IServiceProvider. Services are typically registered at the app's start-up and appended to an IServiceCollection. Once all services are added, you use BuildServiceProvider to create the service container.
- 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.
Code Sample - After Dependency Injection
Dependency Injection visualization
DI enables decoupling and supports development of loosely-coupled code. DI supports implementation of two related concepts, inversion of control and the dependency inversion principle.
- Inversion of Control
- - High-level modules should not depend on low-level modules.
- Dependency Inversion Principle
- - A framework controls which code is executed next, not your code.
Working with Dependency Injection
DI Configuration consist of two main phases, Registration phase and Resolving Phase.
Steps
- Install Microsoft.Extensions.Hosting Nuget Package.
Register services using
Code Sample - Registering Services
- Resolve services using Host.Services.GetRequiredService<YourService>();
Phases of Dependency Injection
Registration Phase
Register types in container so it knows of their existence and when to construct them.
- Register types for later use.
- Indirection through service type and implementing type.
- Choose a lifetime.
Registering Types
When registering types you specify the lifetime, the requested service type and the implementing type. If these types are the same you provide it once.
Code Sample - Registering Types
Resolving Phase
Container is responsible for instantiating types and providing them when requested.
- Resolves and creates types directly.
- Provides dependencies of types you work with.
- Provides dependencies to dependencies of the types you work with.
- Manage the lifetimes of the types.
Resolving Types
When resolving a type, you request an instance of service type. The container will find the implementing type, instantiate it if needed, and return it to you.
If the implementing type has dependencies, they are provided to the implementing type as well.
Code Sample - Resolving Types
Advantages
- No longer calling constructors.
- Container calls the constructors.
- Not concerned with ordering registrations.
- Not concerned with dependencies of each type.
Summary
In this article, we learn't what is Dependency Injection in .NET. We understood how classes are invoked in traditional flow using new() keyword and how dependency injection helped us to avoid the new() glue and makes it easy to manage dependencies. In next article lets talk about lifetimes in dependency injection in .NET.