ASP.NET Core筆記(2) - 依賴注入

  • 服務的生命週期
  • 鏈式注入時,生存期的選擇
  • TryAdd與泛型注入
  • 替換內置服務容器

ASP.NET Core提供了默認的依賴注入容器,可以在Startup.ConfigureServices方法中進行服務注入的配置。

服務的生命週期

默認的依賴注入容器提供了三種生命週期:

  • 暫時(AddTransient),每次在向服務容器進行請求時都會創建新的實例,這種生存期適合輕量級、 無狀態的服務。
  • 範圍內(AddScoped),爲每個客戶端請求創建一次實例。
  • 單例(AddSingleton),第一次請求時(或者在運行Startup.ConfigureServices 並且使用服務註冊指定實例時)創建,每個後續請求都使用相同的實例。如果應用需要單例行爲,建議允許服務容器管理服務的生存期。 不要再自己實現單例設計模式。

下面試驗一下這三種方式的差異:
注入配置:

services.AddSingleton<ISingletonTest, SingletonTest>();
services.AddTransient<ITransientTest, TransientTest>();
services.AddScoped<IScopedTest, ScopedTest>();

services.AddTransient<ScopeAndTransientTester>();

控制器:

public DefaultController(ISingletonTest singleton, ITransientTest transient, IScopedTest scoped, ScopeAndTransientTester scopeAndTransientTester
    )
{
    this.singleton = singleton;
    this.transient = transient;
    this.scoped = scoped;
    this.scopeAndTransientTester = scopeAndTransientTester;
}

[HttpGet]
public string Get()
{
    return $"singleton={singleton.guid}; \r\nscoped1={scoped.guid};\r\nscoped2={scopeAndTransientTester.ScopedID()};\r\ntransient={transient.guid};\r\ntransient2={scopeAndTransientTester.TransientID()};";
}

用於第二次注入的ScopeAndTransientTester類:

public class ScopeAndTransientTester
{
    public ISingletonTest singleton { get; }
    public ITransientTest transient { get; }
    public IScopedTest scoped { get; }

    public ScopeAndTransientTester(ISingletonTest singleton, ITransientTest transient, IScopedTest scoped)
    {
        this.singleton = singleton;
        this.transient = transient;
        this.scoped = scoped;
    }

    public Guid SingletonID()
    {
        return singleton.guid;
    }
    public Guid TransientID()
    {
        return transient.guid;
    }
    public Guid ScopedID()
    {
        return scoped.guid;
    }
}

第一次請求:

singleton=ebece97f-bd38-431c-9fa0-d8af0419dcff; 
scoped1=426eb574-8f34-4bd3-80b3-c62366fd4c74;
scoped2=426eb574-8f34-4bd3-80b3-c62366fd4c74;
transient=98f0da06-ba8e-4254-8812-efc19931edaa;
transient2=c19482f7-1eec-4b97-8cb2-2f66937854c4;

第二次請求:

singleton=ebece97f-bd38-431c-9fa0-d8af0419dcff; 
scoped1=f5397c05-a418-4f92-8c6d-78c2c8359bb5;
scoped2=f5397c05-a418-4f92-8c6d-78c2c8359bb5;
transient=59ed30fa-609b-46b1-8499-93a95ecd330b;
transient2=d2a8ea1c-ae0b-4732-b0a1-ca186897e676;

用Guid來表示不同的實例。對比兩次請求可見AddSingleton方式的id值相同;AddScope方式兩次請求之間不同,但同一請求內是相同的;AddTransient方式在同一請求內的多次注入間都不相同。

TryAdd與泛型注入

另外還有TryAdd{Lifetime}方式,如果只希望在同類型的服務尚未註冊時才添加服務,可以使用這種方法。如果直接使用Add{Liffetime},則多次使用會重複註冊。

除了Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()這種用法,還可以這另一個重載寫法:

Add{LIFETIME}(typeof(SERVICE, typeof(IMPLEMENTATION)

這種寫法還有個好處是可以解析泛型,像ILogger就是框架利用這種方式自動註冊的:

services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));

鏈式注入時,生存期的選擇

以鏈式方式使用依賴關係注入時,每個請求的依賴關係相應地請求其自己的依賴關係。容器會解析這些依賴關係,構建“依賴關係樹”,並返回完全解析的服務。在鏈式注入時需要注意依賴方的生命週期不能大於被依賴方的生命週期。
前面例子中的ScopeAndTransientTester把三種生命週期的服務都注入了,那麼它就只能註冊爲暫時的,否則啓動時會報錯:

System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor '*** Lifetime: Singleton ImplementationType: ***': Cannot consume scoped service '***' from singleton 

替換內置服務容器

內置的服務容器旨在滿足框架和大多數開發者應用的需求,一般使用內置容器就已足夠,除非需要使用某些不受內置容器支持的特定功能如屬性注入、基於名稱的注入、子容器、自定義生存期管理、對遲緩初始化的 Func 支持、基於約定的註冊等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章