最近在 Code Review 時主管提到關於 ASP.NET Core 的一些基本觀念,發現自己對於 .Core DI Service lifetime 部分有些模糊,在 .NET Core 中整個框架建立在 DI ( Dependency Injection) 之上,到處都可以看到 DI 的影子,因此決定寫篇文章釐清自己的觀念,若有問題或是有更推薦的方法歡迎提出一起討論或是給予指導。
.NET Core DI
.NET Core 有提供內建 DI Framework,命名空間為 Microsoft.Extensions.DependencyInjection ,使用 DI 容器服務第一步需要先做註冊,在 .NET Core DI 是在 startup.cs 中的 ConfigureServices 使用 IServiceCollection 的擴充方法指定服務容器生命週期 (LifeTime),預設提供的擴充方法與說明為
- Transient : 每次請求時都會產生新的 Instance
- Scoped : 每個 http Request 都會產生一份 Instance
- Singleton : 整個 Application 只會有一份 Instance
首先,先建立.NET Core Console 專案,專案建立完畢後新增一個 interface 為 IOperation,其屬性 OperationId 為 guid 型別為唯一識別碼,以及其他不同生命週期的 interface 分別實作基本 IOperation 介面
接著新增 Operation 類別並實作不同生命週期的介面,並定義如果未提供 guid 時自己產生一份新的 guid
- public interface IOperation
- {
- Guid OperationId { get; }
- }
- public interface IOperationTransient : IOperation{}
- public interface IOperationScoped : IOperation{}
- public interface IOperationSingleton : IOperation{}
- public interface IOperationSingletonInstance : IOperation{}
接下來我們在 program.cs main 方法新增 ServiceCollection instance 並註冊 IOperationScoped、IOperationTransient、IOperationSingleton 到對應的 AddScoped、AddTransient、AddSingleton 生命週期中,接著透過 service.BuildServiceProvider 方法新增 scope
- public class Operation : IOperationTransient,
- IOperationScoped,
- IOperationSingleton,
- IOperationSingletonInstance
- {
- public Operation() : this(Guid.NewGuid())
- {
- }
- public Operation(Guid id)
- {
- OperationId = id;
- }
- public Guid OperationId { get; private set; }
- }
- using Microsoft.Extensions.DependencyInjection;
- using System;
- namespace EFCoreConsoleApp
- {
- class Program
- {
- static void Main(string[] args)
- {
- var services = new ServiceCollection()
- .AddScoped<IOperationScoped, Operation>()
- .AddTransient<IOperationTransient, Operation>()
- .AddSingleton<IOperationSingleton, Operation>();
- var provider = services.BuildServiceProvider();
- using (var scope1 = provider.CreateScope())
- {
- var p = scope1.ServiceProvider;
- Console.WriteLine("<SCOPE 1>");
- Console.WriteLine("---Test 1---");
- Console.WriteLine($"transient1: {p.GetService<IOperationTransient>().OperationId}");
- Console.WriteLine($"scope1 : { p.GetService<IOperationScoped>().OperationId }");
- Console.WriteLine($"singleton1: {p.GetService<IOperationSingleton>().OperationId}");
- Console.WriteLine(" ");
- Console.WriteLine("---Test 2---");
- Console.WriteLine($"transient2: {p.GetService<IOperationTransient>().OperationId}");
- Console.WriteLine($"scope2 : { p.GetService<IOperationScoped>().OperationId }");
- Console.WriteLine($"singleton2: {p.GetService<IOperationSingleton>().OperationId}");
- Console.WriteLine(" ");
- }
- using (var scope2 = provider.CreateScope())
- {
- var p = scope2.ServiceProvider;
- Console.WriteLine("<SCOPE 2>");
- Console.WriteLine("---Test 3---");
- Console.WriteLine($"transient3: {p.GetService<IOperationTransient>().OperationId}");
- Console.WriteLine($"scope3 : { p.GetService<IOperationScoped>().OperationId }");
- Console.WriteLine($"singleton3: {p.GetService<IOperationSingleton>().OperationId}");
- }
- Console.ReadKey();
- }
- }
- }
輸出如下
透過以上測試與觀察輸出結果,可以得到以下結論
- <SCOPE 1>
- ---Test 1---
- transient1: ee48347a-a819-402b-8e4b-2d0e6a480186
- scope1 : b7d9f907-6f95-4cbb-8ccb-3a2fb247287a
- singleton1: 0c92f569-2b89-4be9-a57c-5d0fc2dd8779
- ---Test 2---
- transient2: 03a58729-7efe-4394-a9ae-0197e4a14e5c
- scope2 : b7d9f907-6f95-4cbb-8ccb-3a2fb247287a
- singleton2: 0c92f569-2b89-4be9-a57c-5d0fc2dd8779
- <SCOPE 2>
- ---Test 3---
- transient3: 7c7334af-62e4-4085-b7a5-9876865412c1
- scope3 : fe99001b-3566-4850-bc17-6757e7051eeb
- singleton3: 0c92f569-2b89-4be9-a57c-5d0fc2dd8779
- Transient : 每次請求時都會產生新的 Instance > 每次都不一樣
- Scoped : 每個 http Request 都會產生一份 Instance > 相同 Scoped 時會相同,不同 Scoped 時會不一樣
- Singleton : 整個 Application 只會有一份 Instance > 整個應用程式一份
使用情境
簡單來說,可以依據情境做不同的生命週期註冊的動作,舉例當 EF Core 中的 DBContext 當未設定生命週期時預設就會是 Scoped 使用,可以跨 Service 不用重複 new instance;如果今天是與 Redis 建立單一連線情境,避免建立過多連線造成 Timeout,這時候就是用到 Singletion 設定;如果是每次 request 都是獨一的就可以使用 transient,當你工作上有需要都可以思考看看哪種生命週期最適合。
參考
.NET Core 中的相依性插入
全面理解 ASP.NET Core 依赖注入
0 意見:
張貼留言