一文講清依賴注入#
先進行一個自我催眠,記住一點依賴注入是一種設計模式
看一個簡單的例子:
// 1. 強依賴關係
public int MyStringTool(){
string str = "Hello world!";
return str.Count();
}
// 2. 無依賴關係
string str = "Hello world!";
public int MyStringTool(string str){
return str.Count();
}
兩個函數,一個是在函數內部創建局部變量,一個是通過參數傳遞變量。前者變量和函數具有強依賴關係(體現在局部變量只能在函數內部起作用)。後者變量通過參數傳遞給函數,函數具有了泛化處理能力,且變量與函數也沒有依賴關係。
這就是依賴注入的基本思想。在類之間的衍生則是下面的例子:
// 1. 強依賴關係
public class MainObject{
public Method(){
var tool = new MyTool();
// 使用tool
}
}
// 2. 依賴注入
public class MainObject{
public readonly MyTool _tool;
MainObject(MyTool tool){
_tool = tool;
}
public Method(){
_tool.function();
// 使用tool
}
}
兩種調用工具類 Tool 的方式,第一種在類內部實例化工具 MyTool,兩者具有強依賴關係;第二種通過構造函數傳遞工具 MyTool 的實例,實現了依賴注入,即 依賴項 MyTool 通過構造函數 注入 進類 MainObject。
還有一個名詞叫控制反轉,我認為這個詞更為貼切。
控制代表兩者間的依賴關係,誰控制誰 / 誰依賴於誰(存在)
在第一個函數例子中,變量與函數的關係從 變量依賴於函數存在 / 函數控制著變量的生命週期 轉換為 函數必須提供參數才能運行
在第二個類例子中,工具類與主體類的關係從 工具實例由主體類創建,並依賴與主體類存在 轉換為 主體類的構造必須提供工具類 。這就體現了 反轉 的含義。當然類具有默認構造函數,並不是非得提供工具類不可。
在 ASP.NET 中的依賴注入#
ASP.net 主體就是 WebApplication
類。它的工具類就是一系列 Services 服務
下面是一個典型的 ASP.net 應用的入口程序(Program.cs)
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var provider = builder.Services.BuildServiceProvider();
var app = builder.Build();
app.UseAuthorization();
app.MapControllers();
app.Run()
在這個樣例程序裡,Services
就是通過依賴注入的方式為WebApplication
提供服務的。
在本 Web 應用程序中,有一個獨立的,為 App 提供功能支持的容器,這些容器之間,可以通過構造函數注入的方式相互獲取實例。下面是依賴注入(DI)涉及到的關鍵的類和接口:
-
IServiceCollection:這是用於註冊服務的接口。您可以使用它的擴展方法來註冊各種服務類型和實現。
-
IServiceProvider:這是一個服務提供程序接口,用於解析和獲取已註冊的服務實例。它是由容器在需要時創建的。
-
ServiceLifetime:這是一個枚舉,用於指定註冊服務的生命週期。常見的生命週期選項包括 Singleton(單例)、Scoped(作用域)和 Transient(瞬時)。
-
AddTransient、AddScoped 和 AddSingleton:這些是
IServiceCollection
接口的擴展方法,用於將服務類型和其實現添加到服務容器中,並指定它們的生命週期。 -
IServiceProvider.GetService<T>:這是
IServiceProvider
接口的方法,用於獲取已註冊的服務實例。您可以通過指定服務類型的泛型參數來獲取特定類型的服務實例。 -
構造函數注入:這是一種常見的依賴關係注入方式,通過將依賴的服務作為參數傳遞給類的構造函數來實現。依賴項在創建類實例時自動解析和提供。
-
屬性注入:除了構造函數注入外,您還可以使用屬性注入來注入依賴項。在類中定義需要注入的屬性,並使用相應的屬性注入特性(如
[Inject]
、[Autowired]
等)進行標記。
Tip.
Services.AddXXX()
就是為依賴容器中添加依賴項的方法,這些方法通過 C# 的擴展方法的語法特性提供。該語法可以在不改變類的情況下為其添加額外方法:// 命名空間為 MyNamespace public static int ExMethod(this String str){ return str.Count(); } // 使用 using MyNamespace String str = new String("hello world!"); str.ExMethod();
在 .NET 中,服務是應用程序的依賴項,可以使用依賴注入(DI)進行管理。服務可以是在應用程序中定義的類、接口或其他組件,它們提供特定的功能或服務。
當一個類需要使用其他服務時,可以通過構造函數注入的方式直接引入這些依賴項。通過將依賴項作為構造函數參數聲明,DI 容器會自動檢測到這些依賴項,並在創建類的實例時自動提供它們。
示例:
public class MyService
{
private readonly IAnotherService _anotherService;
public MyService(IAnotherService anotherService)
{
_anotherService = anotherService;
}
public void DoSomething()
{
// 使用依賴項進行操作
_anotherService.SomeMethod();
}
}
在上面的示例中,MyService
類需要使用 IAnotherService
接口的實現。通過將 IAnotherService
作為構造函數參數聲明,DI 容器會在創建 MyService
的實例時自動提供它。
對於外部(靜態)類或方法,如果需要使用依賴項,可以通過 IServiceProvider
接口來獲取它們。IServiceProvider
是一個服務提供程序接口,用於解析和獲取已註冊的服務實例。
示例:
public static class MyStaticClass
{
public static void DoSomething(IServiceProvider serviceProvider)
{
// 通過IServiceProvider獲取依賴項
var anotherService = serviceProvider.GetService<IAnotherService>();
// 使用依賴項進行操作
anotherService.SomeMethod();
}
}
在上面的示例中,MyStaticClass
是一個靜態類,它的方法 DoSomething
需要使用 IAnotherService
的實例。通過將 IServiceProvider
作為參數傳遞給 DoSomething
方法,可以使用 GetService
方法從 IServiceProvider
中獲取 IAnotherService
的實例。
總結來說,通過依賴注入和 IServiceProvider
,我們可以在構造函數中直接引入依賴項,並讓 DI 容器自動提供;對於外部(靜態)類或方法,可以通過 IServiceProvider
獲取依賴項,實現依賴項的使用和解耦。
依賴項生命週期#
當我們把Services
用依賴容器統一管理起來,在需要用到某個依賴項時由容器提供。這樣就產生一個問題,依賴項什麼時候實例化?是需要的時候重新創建;還是持久化一個實例;亦或是根據請求者的作用域提供實例?這就是依賴項的生命週期。
在依賴注入(DI)容器中,實例的創建可以根據配置和需要的時機進行不同的處理。一般來說,DI 容器在應用啟動時會進行初始化,並根據需要預先創建和註冊一些實例。具體的創建時機和生命週期可以通過配置來控制。
常見的實例生命週期包括:
- Singleton(單例):在整個應用程序生命週期中只創建一個實例,並在需要時重用。該實例通常在應用程序啟動時被創建。
- Scoped(作用域):在每個作用域(例如每個請求)內創建一個實例,並在該作用域內重用。在每個作用域結束時,實例將被銷毀。
- Transient(瞬態):每次請求時都創建一個新的實例。每個請求都會得到一個新的實例,並在請求完成後被銷毀。
這些生命週期選項允許您根據應用程序的需要來管理實例的創建和生命週期。您可以在註冊服務時指定所需的生命週期選項,例如使用 AddSingleton
、AddScoped
或 AddTransient
方法。
其他#
除了 ASP.NET Core 默認提供的依賴注入容器,你還可以選擇使用其他第三方的依賴注入容器。以下是一些常見的依賴注入容器:
- Autofac: Autofac 是一個流行的、功能強大的依賴注入容器,它提供了許多高級功能和擴展點,適用於各種應用場景。
- Unity: Unity 是 Microsoft 提供的一個輕量級的依賴注入容器,它具有良好的性能和可擴展性,並支持面向接口的開發。
- Ninject: Ninject 是另一個流行的依賴注入容器,它注重簡潔性和易用性,提供了一種簡單而靈活的方式來管理對象之間的依賴關係。
- StructureMap: StructureMap 是一個功能強大的依賴注入容器,它支持構造函數注入、屬性注入和方法注入,並提供了許多高級功能,如攔截器和配置文件。
這些依賴注入容器都有各自的特點和優勢,你可以根據項目需求和個人偏好選擇適合的容器。在使用這些容器時,你需要按照它們的文檔和示例進行相應的配置和使用。
需要注意的是,如果你選擇使用第三方的依賴注入容器,你可能需要安裝相應的 NuGet 包,並進行適當的配置。同時,你也需要了解這些容器的特定語法和用法,以正確地註冊和解析依賴項。
總之,ASP.NET Core 默認的依賴注入容器已經足夠滿足大多數應用的需求。但如果你需要更高級的功能或特定的需求,可以考慮使用其他的依賴注入容器。