[ASP.NET Core學習筆記]02-相依性注入(Dependency Injection)
相依性注入(Dependency Injection)
用來在類別與相依性之間,達成控制反轉的技術。可以讓類別相依於抽象,而不是實作,進以降低類別耦合度。
服務生命週期(Service Lifetime)
ASP.NET Core可以設定以下幾個服務生命週期
- Transient: 在每一次的Request都會建立
- Scoped: 在每一次的Request中只會建立一次
- Singleton: 第一次收到要求時,會建立一個實體,後續的要求會使用同一個實體
實際以Code的方式來體驗一下這三個生命週期的差別
我建立了TestScopedService
、TestSingletonService
、TestTransientService
,並建立相對應的Interface,實作GetGuid()
方法
代表三種不同生命周期的類別,以ITestScopedService
為例,其餘以此類推
ITestScopedService
介面
public interface ITestScopedService
{
Guid GetGuid();
}
TestScopedService
類別
public class TestScopedService : ITestScopedService
{
private readonly Guid _guid;
public TestScopedService()
{
this._guid = Guid.NewGuid();
}
public Guid GetGuid()
{
return this._guid;
}
}
建立好之後在Startup.ConfigureServices
中註冊
services.AddTransient<ITestTransientService, TestTransientService>();
services.AddScoped<ITestScopedService, TestScopedService>();
services.AddSingleton<ITestSingletonService, TestSingletonService>();
並在Controller與自訂的Service中使用這三個類別,回傳的Guid結果如下
Request第一次的結果
{
"controller": {
"scoped": "9251708d-b037-4134-a61e-3ac257fda230",
"transient": "d4110587-b117-4972-8a75-0ff3a010842f",
"singleton": "a306ad7b-e3b6-4953-b519-235f01a84c02"
},
"services": {
"scoped": "9251708d-b037-4134-a61e-3ac257fda230",
"transient": "f75d74db-c5ad-4e1c-8b81-bbcb6717b368",
"singleton": "a306ad7b-e3b6-4953-b519-235f01a84c02"
}
}
Request第二次的結果
{
"controller": {
"scoped": "59b4e771-65a9-4113-a690-98feaa34a8cc",
"transient": "22c3dc54-146a-4830-b0b1-6a85e0fc8b20",
"singleton": "a306ad7b-e3b6-4953-b519-235f01a84c02"
},
"services": {
"scoped": "59b4e771-65a9-4113-a690-98feaa34a8cc",
"transient": "78edee78-214c-4b34-936e-1ee6ecf1ac92",
"singleton": "a306ad7b-e3b6-4953-b519-235f01a84c02"
}
}
在接收到Request建立實體時,Scoped
會建立一個實體,直到Request結束時都使用同一個。
而Transient
則是在每一次要使用服務時建立實體都會是新的。Singleton
則是只會建立一個實體,
在接收到不管幾個要求都是使用同一個實體。
針對相依性注入設計服務的注意事項
- 設計
服務(Service)
時使用DI的方式取得相依性 - 避免
static
方法呼叫 - 避免在
服務(Service)
中直接new class
。這樣會耦合特定實作 - 類別盡量維持小型且容易測試的狀態
- 如果是以程式碼加入類別實體到DI Container,Container不會自動Dispose,例如
services.AddSingleton<Service3>(new Service3());
相依性注入的最佳作法
- C#不支援
非同步建構式(Construtor)
,建議是以同步
方式解析服務後使用服務的非同步方法
- 避免在
服務容器(Container)
中儲存或使用資料與設定,例如組態設定。 - 避免使用
GetService
來取得實體 - 避免使用靜態方式存取服務,例如用
IApplicationBuilder.ApplicationServices
取得 - 避免用靜態方式存取
HttpContext
,(例如 IHttpContextAccessor.HttpContext)