在 .NET Core 中,依賴注入 (DI) 容器可以根據三種不同的生命週期來管理服務: - **Singleton**:單例生命週期意味著 DI 容器只會創建一個服務的實例,並在整個應用程式的生命週期內使用該實例。 - **Scoped**:作用域生命週期意味著 DI 容器會為每個 HTTP 請求創建一個新的服務實例。 - **Transient**:瞬時生命週期意味著 DI 容器會在每次需要服務時創建一個新的實例。 以下是一些有關這三種生命週期的具體細節: ## Singleton - 單例生命週期是 .NET Core 中最常用的生命週期。 - 適合於在整個應用程式中使用的服務,例如配置設定、資料庫連線和緩存。 - 使用 AddSingleton() 方法將服務註冊到 DI 容器中。 AddSingleton() 方法用於將服務註冊到 DI 容器中,並使用單例生命週期。這意味著 DI 容器只會創建一個服務的實例,並在整個應用程式的生命週期內使用該實例。 **使用 AddSingleton() 方法的時機:** * 服務在整個應用程式中都需要使用,例如配置設定、資料庫連線和緩存。 * 服務的狀態不需要在每次使用時都保持一致,例如隨機數生成器和雜湊函式。 **以下是一些使用 AddSingleton() 方法的具體範例:** * 儲存配置設定 * 提供資料庫連線 * 實現緩存 * 生成隨機數 * 計算雜湊值 **使用 AddSingleton() 方法的優點:** * 提高效能,因為 DI 容器只會創建一個服務的實例。 * 減少記憶體使用量,因為 DI 容器只會在記憶體中保留一個服務的實例。 **使用 AddSingleton() 方法的缺點:** * 服務的狀態在整個應用程式的生命週期內都是一致的。這可能不是在所有情況下都需要的。 * 服務的實例不能被替換。 **以下是一些使用 AddSingleton() 方法時需要注意的事項:** * 確保服務是無狀態的,或者其狀態在整個應用程式的生命週期內都是一致的。 * 考慮使用其他生命週期,例如 Scoped 或 Transient,如果服務的狀態需要在每次使用時都保持一致。 ## Scoped - 作用域生命週期通常用於在 HTTP 請求的上下文中使用的服務。 - 適合於需要訪問 HTTP 請求上下文資訊的服務,例如使用者資訊和安全資訊。 - 使用 AddScoped() 方法將服務註冊到 DI 容器中。 AddScoped() 方法用於將服務註冊到 DI 容器中,並使用作用域生命週期。這意味著 DI 容器會為每個 HTTP 請求創建一個新的服務實例。 **使用 AddScoped() 方法的時機:** * 服務需要訪問 HTTP 請求上下文資訊,例如使用者資訊和安全資訊。 * 服務的狀態需要在每次 HTTP 請求中都保持一致。 **以下是一些使用 AddScoped() 方法的具體範例:** * 驗證使用者 * 獲取使用者資訊 * 檢查安全性 **使用 AddScoped() 方法的優點:** * 確保服務的狀態在每次 HTTP 請求中都保持一致。 * 允許服務訪問 HTTP 請求上下文資訊。 **使用 AddScoped() 方法的缺點:** * 可能會降低效能,因為 DI 容器會為每個 HTTP 請求創建一個新的服務實例。 * 可能會增加記憶體使用量,因為 DI 容器會在記憶體中保留每個 HTTP 請求的服務實例。 **以下是一些使用 AddScoped() 方法時需要注意的事項:** * 確保服務是無狀態的,或者其狀態在每次 HTTP 請求中都保持一致。 * 考慮使用其他生命週期,例如 Singleton 或 Transient,如果服務的狀態不需要在每次 HTTP 請求中都保持一致。 ## Transient - 瞬時生命週期通常用於不需要任何狀態的服務。 - 適合於每次使用都應該產生新結果的服務,例如隨機數生成器和雜湊函式。 - 使用 AddTransient() 方法將服務註冊到 DI 容器中。 AddTransient() 方法用於將服務註冊到 DI 容器中,並使用瞬時生命週期。這意味著 DI 容器會在每次需要服務時創建一個新的服務實例。 **使用 AddTransient() 方法的時機:** * 服務每次使用都應該產生新結果,例如隨機數生成器和雜湊函式。 * 服務不需要任何狀態,或者其狀態不需要在每次使用時都保持一致。 **以下是一些使用 AddTransient() 方法的具體範例:** * 隨機數生成器 * 雜湊函式 * GUID 生成器 **使用 AddTransient() 方法的優點:** * 確保每次使用服務時都會生成新的結果。 * 減少記憶體使用量,因為 DI 容器只會在需要時創建服務實例。 **使用 AddTransient() 方法的缺點:** * 可能會降低效能,因為 DI 容器會在每次需要服務時創建一個新的服務實例。 **以下是一些使用 AddTransient() 方法時需要注意的事項:** * 確保服務是無狀態的,或者其狀態不需要在每次使用時都保持一致。 * 考慮使用其他生命週期,例如 Singleton 或 Scoped,如果服務的狀態需要在每次使用時都保持一致。 **以下是一些不適合使用 AddTransient() 方法的範例:** * 資料庫連線 * 緩存 * 設定 這些服務通常需要在應用程式的生命週期內保持相同的狀態。使用 AddTransient() 方法會導致這些服務的狀態在每次使用時都不同,這可能導致意外的結果。 ## 範例 以下是一個示例,展示了如何使用這三種生命週期: ``` csharp= // 定義一個訂單服務的介面 public interface IOrderService { string GetId(); } // 實現訂單服務的介面 public class OrderService : IOrderService { // 在構造函數中生成一個唯一的標識符 private readonly string _id; public OrderService() { _id = Guid.NewGuid().ToString(); } // 返回標識符 public string GetId() { return _id; } } // 在 Startup.cs 中註冊不同生命週期的服務 public class Startup { public void ConfigureServices(IServiceCollection services) { // 註冊 Singleton 服務 services.AddSingleton<IOrderService, OrderService>(); // 註冊 Scoped 服務 services.AddScoped<IOrderService, OrderService>(); // 註冊 Transient 服務 services.AddTransient<IOrderService, OrderService>(); } } // 在 Controller 中注入和使用服務 [ApiController] [Route("[controller]")] public class OrderController : ControllerBase { // 注入三種不同生命週期的服務 private readonly IOrderService _singletonService; private readonly IOrderService _scopedService; private readonly IOrderService _transientService; public OrderController( [FromServices] IOrderService singletonService, [FromServices] IOrderService scopedService, [FromServices] IOrderService transientService) { // 賦值給私有字段 _singletonService = singletonService; _scopedService = scopedService; _transientService = transientService; } // 定義一個 GET 方法,返回服務的標識符 [HttpGet] public IActionResult Get() { // 獲取服務的標識符 var singletonId = _singletonService.GetId(); var scopedId = _scopedService.GetId(); var transientId = _transientService.GetId(); // 返回結果 return Ok(new { Singleton = singletonId, Scoped = scopedId, Transient = transientId }); } } ``` 這個範例的返回結果是一個 JSON 物件,包含了三種不同生命週期的服務的標識符。例如: ```jsonl= { "Singleton": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6", "Scoped": "q7r8s9t0-u1v2-w3x4-y5z6-a7b8c9d0e1f2", "Transient": "g1h2i3j4-k5l6-m7n8-o9p0-q1r2s3t4u5v6" } ``` 這個 JSON 物件顯示了每次請求時,服務的標識符是如何變化的。 **Singleton** 服務的標識符在整個應用程序生命週期內保持不變,**Scoped** 服務的標識符在每個請求的作用域內保持不變,而 **Transient**服務的標識符每次請求都會改變。這些標識符是隨機生成的,所以每次執行時可能會有不同的值,但是它們的變化規律是一致的。