[ASP.NET Core學習筆記]02-相依性注入(Dependency Injection)

[ASP.NET Core學習筆記]02-相依性注入(Dependency Injection)

相依性注入(Dependency Injection)

用來在類別與相依性之間,達成控制反轉的技術。可以讓類別相依於抽象,而不是實作,進以降低類別耦合度。

服務生命週期(Service Lifetime)

ASP.NET Core可以設定以下幾個服務生命週期

  • Transient: 在每一次的Request都會建立
  • Scoped: 在每一次的Request中只會建立一次
  • Singleton: 第一次收到要求時,會建立一個實體,後續的要求會使用同一個實體

實際以Code的方式來體驗一下這三個生命週期的差別

我建立了TestScopedServiceTestSingletonServiceTestTransientService,並建立相對應的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)

我的練習範例 02-DependencyInjection