Note: this article is published on 07/10/2024.
These will be a series of articles about Design Patterns. We start from MVC Pattern:
A: 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. Inversion of Control is a principle in software engineering which transfers the control of objects or portions of a program to a container or framework.
This article will be an implementation of Dependency Injection for a MVC app with a constructor injection and associated with a .NET Core built in container to handle the service lifetimes. This article has the same structure with Design Pattern (5-3), Dependency Injection, Console Demo, just with the different platform.
- A: Introduction
- B: Setup Environment
- C: Setup the Program
- D: Run and Result
B: Setup Environment:
Open a .NET Core Web App (MVC) by Visual Studio 2022, Version 17.10.3, current on 07/07/2024
![]()
Project Name as TransientScopedSingleton
![]()
Framework: .NET 8.0 (Long Term Support)
![]()
Running:
![]()
This is the default program.cs:
![]()
C: Setup the Program:
After the program is setting up, we will have these:
![]()
Now we go step by step according to the process of setting up a Dependency Injection,
- Setup Interface
- Make a service
- Register the service to a Container (.Net Core Default DI Container)
- Make a client
1. Setup Interfaces:
- ICommonService --- Line 3
- A
Guid Id
property that represents the unique identifier of the service.
- ITransientService : ICommonService --- Line 7
- IScopedService : ICommonService --- Line 10
- ISingletonService : ICommonService --- Line 13
![]()
Note:
For convenience, I combine the four saperated pages for interfaces in one as above:
![]()
2. Make Services: in OperationService.cs
Implementations for the interfaces: Initializing their Id
property with the result of Guid.NewGuid().
- public class OperationService: ITransientService, IScopedService, ISingletonService: Line 3
![]()
3. Register Services for DI (.NET Core default DI container): in program.cs
- builder.Services.AddTransient<ITransientService, OperationService>(); --- Line 11
- builder.Services.AddScoped<IScopedService, OperationService>(); --- Line 12
- builder.Services.AddSingleton<ISingletonService, OperationService>(); --- Line 13
![]()
4. Make a Client: in program.cs
Use HomeController that requires DI
- public HomeController(ILogger<HomeController> logger,
ITransientService transientService1,
ITransientService transientService2,
IScopedService scopedService1,
IScopedService scopedService2,
ISingletonService singletonService1,
ISingletonService singletonService2) --- Line 16~21
The HomeControllerdefines a constructor that requires each of the aforementioned service interfaces, that is, ITransientService , IScopedService
, and ISingletonService
. The object exposes a single method that allows the consumer to useon the service with a given lifetimeDetails
parameter.
![]()
D: Run and Result
Run the project and we made three requests from browser. In each request from the brwoser, the app will make twice requests from the DI container with each service lifetime:
- Transient
- Scoped
- Singleton
such as
![]()
The reaults are
![]()
Request 1
![]()
Request 2
![]()
Request 3
From the app output, you can see that:
- Transient services are always different in each browser request and each container request, a new instance is created with every retrieval of the service.
- Scoped services change only with a new scope created by browser request, but are the same instance within a scope (each browser request).
- Singleton services are always the same (in the app lifetime), a new instance is only created once.
These results can be demoed by the following graph for each container request:
![]()
References: