在 .Net 及 .Net Core 的相依注入 (Dependency Injection) 中,
服務可以被註冊成以下三種存留期:
下面跟大家介紹這三種存留期的特性,及如何選擇該使用的存留期。
public class Startup { // ... public void ConfigureServices(IServiceCollection services) { // 註冊 SampleService 為 SampleService services.AddTransient(typeof(SampleService)); services.AddTransient<SampleService>(); services.AddTransient(serviceProvider => new SampleService(serviceProvider.GetService<SampleInjectService>()); // 註冊 ISampleService 為 SampleService services.AddTransient(typeof(ISampleService), typeof(SampleService)); services.AddTransient(typeof(ISampleService), serviceProvider => new SampleService(serviceProvider.GetService<SampleInjectService>()); services.AddTransient<ISampleService, SampleService>(); services.AddTransient<ISampleService>(serviceProvider => new SampleService(serviceProvider.GetService<SampleInjectService>()); } // ... }
被註冊為 Transient 的服務,會在每次向 Container 請求注入時,建立一個新的實例。
也就是說,假設 A 及 B 都有注入被註冊為 Transient 的 C Service,
則注入到 A 和 B 中的 C Service 會是兩個不同的實例。
Transient 的實例會在每次請求結束後被 Dispose。
註冊成 Transient 的服務,存留時間短,適合輕量、無狀態 (Stateless) 的服務。
public class Startup { // ... public void ConfigureServices(IServiceCollection services) { // 註冊 SampleService 為 SampleService services.AddScoped(typeof(SampleService)); services.AddScoped<SampleService>(); services.AddScoped(serviceProvider => new SampleService(serviceProvider.GetService<SampleInjectService>()); // 註冊 ISampleService 為 SampleService services.AddScoped(typeof(ISampleService), typeof(SampleService)); services.AddScoped(typeof(ISampleService), serviceProvider => new SampleService(serviceProvider.GetService<SampleInjectService>()); services.AddScoped<ISampleService, SampleService>(); services.AddScoped<ISampleService>(serviceProvider => new SampleService(serviceProvider.GetService<SampleInjectService>()); } // ... }
Scoped 是針對 Web 應用程式使用的存留期。
被註冊為 Scoped 的服務,會在每個客戶端請求連線時,建立一個新的實例。
也就是說,假設 A 及 B 都有注入被註冊為 Scoped 的 C Service,
則在處理同一個 API Request 時,注入到 A 和 B 中的 C Service 會是相同的實例;
但在處理另一個 API Request 時,注入的 C Service 會是不同的實例。
Scoped 的實例同樣會在每次請求結束後被 Dispose。
註冊成 Scoped 的服務,存留時間和客戶端請求相同,適合針對個別請求有不同狀態的服務。
public class Startup { // ... public void ConfigureServices(IServiceCollection services) { // 註冊 SampleService 為 SampleService services.AddSingleton(typeof(SampleService)); services.AddSingleton<SampleService>(); services.AddSingleton(serviceProvider => new SampleService(serviceProvider.GetService<SampleInjectService>()); services.AddSingleton(new SampleService()); // 直接提供實例給Container // 註冊 ISampleService 為 SampleService services.AddSingleton(typeof(ISampleService), typeof(SampleService)); services.AddSingleton(typeof(ISampleService), serviceProvider => new SampleService(serviceProvider.GetService<SampleInjectService>()); services.AddSingleton( typeof(ISampleService), new SampleService()); // 直接提供實例給Container services.AddSingleton<ISampleService, SampleService>(); services.AddSingleton<ISampleService>(serviceProvider => new SampleService(serviceProvider.GetService<SampleInjectService>()); } // ... }
被註冊為 Singleton 的服務,會在第一次向 Container 請求注入時,建立一個新的實例。
(或者是由開發者直接向 Container 提供一個實例)
在實例產生後,應用程式中的每一個注入請求,都會使用同一個實例。
也就是說,只要在同一個應用程式中注入的 C Service 都會是相同的實例。
當應用程式結束、 ServiceProvider 被 Dispose 時,實例才會被 Dispose。
註冊成 Singleton 的服務,存留時間和應用程式相同,適合需要在應用程式執行期間維持狀態的服務。
由於 Scoped 存留期是根據客戶端請求的長度,
故不能在 "註冊為 Singleton 的服務" 中注入 "註冊為 Scoped 的服務"。
亦不可透過註冊為 Transient 的服務,間接注入註冊為 Scoped 的服務。
(Ex. 在 Singleton 服務中注入 "註冊為 Transient 但注入 Scoped 服務" 的服務)