ASP.NET Core - 依賴注入(二)

啊晚發表於2023-02-24
  1. .NET Core 依賴注入的基本用法

話接上篇,這一章介紹 .NET Core 框架自帶的輕量級 Ioc 容器下服務使用的一些知識點,大家可以先看看上一篇文章 [ASP.NET Core - 依賴注入(一)]

2.3 服務解析

透過 IServiceCollection 註冊了服務之後,可以透過以下方式解析相應服務的例項:

  • IServiceProvider
    IServiceProiver 例項由 IServiceCollection 透過 BuildServiceProvider() 方法建立,在 ASP.NET Core 中,主機啟動的時候會建立一個全域性的 IServiceProvider,並且此例項也在容器當中。所有在容器註冊過的服務都可以透過 IServiceProiver 進行解析,當然該服務的依賴項必須也在容器中註冊。
  • ActivatorUtilities
    用於手動建立未在DI容器中註冊的服務例項

2.3.1 服務注入方式

當我們透過容器解析一個服務例項的時候,容器根據當前服務的鏈式依賴關係圖解析其依賴項,根據依賴項的生命週期或建立、或從已有的例項獲取,然後注入到我們解析的服務當中。在一個服務中獲取另一個服務例項的方式由以下幾種:

(1) 建構函式注入

建構函式注入是非常常見的服務注入方式,也是微軟最推薦的方式,這種方式可以明確地宣告當前類所依賴的東西,一目瞭然。如同上面的示例程式碼中,使用的就是建構函式注入方式。建構函式注入,對於類的建構函式有以下要求:

  • 建構函式可以接收非依賴注入的引數,但必須提供預設值

  • 當服務透過 IServiceProvider 解析時,要求建構函式必須是 public

    當服務由 ActivatorUtilities 解析時,建構函式注入要求只存在一個適用的建構函式。 支援建構函式過載,但其引數可以全部透過依賴注入來實現的過載只能存在一個。

  • 如果發現建構函式時存在歧義,將引發異常,例如以下情況:

    public class ExampleService
    {
    	public ExampleService()
    	{
    	}
    
    	public ExampleService(ILogger<ExampleService> logger)
    	{
    		// omitted for brevity
    	}
    
    	public ExampleService(IOptions<ExampleOptions> options)
    	{
    		// omitted for brevity
    	}
    }
    

(2) 屬性注入

這裡有一點需要說明,.NET Core 內建的依賴注入框架並不支援屬性注入,如果需要使用屬性注入需要結合第三方依賴注入框架進行使用,如autofac。

顧名思義,屬性注入就是透過類中的屬性注入需要的服務,要求屬性必須是 public ,並且具備 get、set 訪問器。如下:

image

屬性注入一般用於注入一些即使缺失了也不會導致當前類無法工作的依賴項,如日誌記錄等。這種時候會為資料注入設定一個預設實現,防止該屬性為空,導致當前類的功能受影響。

(3) 方法注入

透過 FromServicesAttribute 特性在控制器的方法引數中注入,這種方式只能用於控制器。預設情況下,控制器示例由容器來管理,在入口檔案呼叫 builder.Services.AddControllers(); 時註冊到容器中。

[HttpGet(nameof(InjectTest3))]
public Task InjectTest3([FromServices] IRabbit rabbit)
{
    Console.WriteLine(rabbit is Rabbit);
    return Task.CompletedTask;
}

這種方式用於縮小依賴注入的粒度,適用於注入的服務只在當前方法使用的時候,是對建構函式注入的簡化。

(4) 手動解析

在.NET框架中,任何可以拿得到 IServiceProvider 例項的地方都可以透過 GetRequiredService() 或者 GetService() 解析我們需要的服務。直接使用 IServiceProvider 是服務定位器模式的一個示例。這通常被認為是反模式,因為它隱藏了類的依賴關係。這種方式在某些情況下是有用的,但是應該儘量避免。

image

GetService() 與 GetRequiredService() 的區別在於解析服務時,如果該服務沒有在容器中註冊,前者會返回Null,而後者會丟擲異常。兩者的區別可參考以下檔案:ASP.NET Core中GetService()和GetRequiredService()之間的區別

除了透過注入 IServiceProvider 來解析服務之外,其他的方式,例如 HttpContext 中也包含 IServiceProvider 例項,如:

var rabbit1 = HttpContext.RequestServices.GetRequiredService<IRabbit>();

2.3.2 ActivatorUtilities 使用

透過 ActivatorUtilities 解析服務比較簡單,常用的由以下兩個方法:

ActivatorUtilities.CreateInstance<HelloService>(provider, "test");
ActivatorUtilities.GetServiceOrCreateInstance<IHelloService>(provider);

其中 CreateInstance 方法的泛型型別需要是具體的型別,而不是介面,這個方法還可以傳入建構函式需要的,但沒有在容器中註冊的引數。GetServiceOrCreateInstance 方法會先嚐試從容器獲取例項,獲取不到再建立,不支援不在容器中註冊的建構函式引數。



參考文章:
ASP.NET Core 依賴注入 | Microsoft Learn
理解ASP.NET Core - 依賴注入(Dependency Injection)



ASP.NET Core 系列:

目錄:ASP.NET Core 系列總結
上一篇:ASP.NET Core - 依賴注入(一)

相關文章