依赖注入在 dotnet core 中实现与使用:1 基本概念
關于 Microsoft Extension: DependencyInjection 的介紹已經很多,但是多數偏重于實現原理和一些特定的實現場景。作為 dotnet core 的核心基石,這里準備全面介紹它的概念、原理和使用。
這里首先介紹概念部分。
1. 概念
該項目在 GitHub 的地址:https://github.com/aspnet/Extensions/tree/master/src/DependencyInjection
Microsoft.Extensions.DependencyInjection?是微軟對依賴倒置原則的實現。作為 ASP.NET Core 的基石,DependencyInjection?貫穿了整個項目的方方面面,掌握它的使用方式和原理,不僅對理解 ASP.NET Core 有重要意義,也有助于將它運用到其它項目的開發中,幫助提供項目開發的效率和質量。
1.1 問題的場景
在軟件開發中,項目通常有多個不同的模塊組成,模塊之間存在依賴關系。例如,我們考慮一個簡化的場景,我們有 3 個關于用戶的類:
AccountController,提供用戶交互界面
UserService,提供用戶管理的業務邏輯
UserRepository,提供用戶管理的數據訪問
AccountController?內部需要使用?UserService?的實例 來管理用戶,而?UserService?內部則需要基于?UserRepository?來提供數據訪問。我們稱它們之間存在依賴關系。或者表達為,AccountController?依賴于?UserService?,而?UserService?依賴于?UserRepository?。而依賴注入就是用來幫助我們實現依賴管理的有力工具。
1.2 依賴倒置原則 DIP
依賴倒置原則是廣為人知的設計原則之一,該原則是實現軟件項目中模塊的解耦的理論基石。
原則的定義如下:
High level modules should not depend upon low level modules,Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstracts.
翻譯過來為:
高層模塊不應該依賴低層模塊,兩者都應該依賴抽象
抽象不應該依賴細節
細節應該依賴抽象
在沒有實現依賴倒置原則的時候,我們通過在?AccountController?類中自己通過?new?關鍵字來創建其依賴的?UserService?對象實例,
public class AccountController {private readonly UserService _userService;
public AccountController() {
this._userService = new UserService();
}
}
這導致了兩個類之間的緊耦合,AccountController?與?UserService?被綁定到一起, 在每次創建?AccountController?的時候,一定會創建一個?UserService?的對象實例,而如果我們需要測試?AccountController?的時候,也就不得不考慮?UserService,這樣一級一級的依賴下來,UserService?又會依賴?UserRepository,就會發現項目中的類都被綁定在一起, 緊密耦合,難以分拆。
基于依賴倒置的原則,通常會考慮通過接口進行隔離。例如,我們可能會定義一個用戶服務的接口:
public interface IUserService{
}
而用戶服務則會實現該接口
public class UserService : IUserService {}
在?AccountController?類中,則改變成了基于接口來使用?UserService。
public class AccountController {private readonly IUserService _userService;
public AccountController() {
this._userService = new UserService();
}
}
雖然在?HomeController?內部,我們可以基于接口編程了,但是這樣的作法并沒有解決自己通過?new?來獲取?UserService?對象實例的問題。
1.3 控制反轉 IoC
IoC是一種著名的實現 DIP 的設計模式。
它的核心思想是:在需要對象實例的時候,不要總考慮自己通過?new?來創建對象,放下依賴對象的創建過程,而是把創建對象的工作交給別人來負責,這個別人我們通常稱為?容器 (Container)?或者?服務提供者 (ServiceProvider), 我們后面使用這個?ServiceProvider?來指代它,
在需要對象實例的時候,從這個?ServiceProvider?中獲取。
下面是一個廣泛使用的示意圖。拿總是要拿的,但是從?自己穿上?變成了?給你穿上
在控制反轉中,引入了一個?ServiceProvider?來幫助我們獲得對象實例。
1.4 依賴注入 DI (DependencyInjection)
DI 是 IoC 模式的一種實現。
《Expert one on one J2EE Development without EJB》第 6 章
IoC 的主要實現方式有兩種:依賴查找,依賴注入 (p128)
依賴注入是一種更可取的方式。(p130)
Martin Fowler 的原文:
As a result I think we need a more specific name for this pattern. Inversion of Control is too generic a term, and thus people find it confusing. As a result with a lot of discussion with various IoC advocates we settled on the name Dependency Injection.
大意是:
已經存在某種模式,該模式被稱為 IoC,但 IoC 太過廣義,任何框架都 IoC,為了讓表意更明確,決定采用 DI 來精確指稱它。
DI 的實現有多種,我們這里介紹的是微軟官方在 Microsoft Extension 中內置提供的 DependencyInjection。它是 IoC 中一種實現,ASP.NET Core 的整個核心基于它來實現。同時,我們也可以在其它項目中使用,以實現對依賴倒置原則的支持。
2. DependencyInjection 中的基本概念
2.1 服務描述集合 ServiceCollection
在微軟的 DI 實現中,所有的服務需要首先注冊到一個公共的服務描述集合中,該集合對于整個 DI 來說,只需要一個,服務只需要在此集合中注冊一次,即可在以后通過 DI 提供給使用者。
該集合的接口定義為?IServiceCollection,可以看出來,它其實就是一個用來保存服務注冊的集合。
public interface IServiceCollection : IList<ServiceDescriptor>, ICollection<ServiceDescriptor>, IEnumerable<ServiceDescriptor>, IEnumerable{
}
系統默認已經實現了一個對?IServiceCollection?的實現,名為?ServiceCollection。在 ASP.NET Core 中,內部會創建該對象的實例,我們也可以在其它項目中,自己來創建它,很簡單,直接?new?出來就可以使用了。
IServiceCollection services = new ServiceCollection ();2.2 服務 Service
在 DI 語境中,服務特指通過 DI 容器管理的對象實例。這個服務并不一定被稱為 **Service,而是可以是任何由 DI 所管理的對象,只是在 DI 這個語境下,我們將其統稱為服務。
服務是我們自己定義的,例如前面提到的?AccountController?和?UserService?等等。
我們通過 DI 來獲得服務對象實例,管理服務對象的生命周期,對于存在復雜依賴關系的對象, DI 還負責管理這些實例之間的依賴關系。
服務必須首先注冊在 DI 中才能使用,但是,注冊前需要首先考慮和決定服務的生命周期。
2.3 服務的生命周期
服務對象實例有著不同類型的生命周期。有些對象的生命周期與應用程序相同,在應用程序啟動時創建,在應用程序退出時才需要釋放。例如我們的數據訪問對象實例。有些對象僅僅在當前方法中使用,在方法調用結束之后就應該銷毀。服務的生命周期管理用來管理這些需求。
DI 支持三種類型的生命周期:
Singleton,單例,在當前應用程序環境下只有一個實例。例如數據訪問服務對象實例。
Scoped,限定范圍,一旦退出此范圍,在此范圍內的服務對象都需要銷毀。例如 Web 開發中的請求對象實例。
Transient,瞬態,一次性使用,每次從 DI 中獲取,都返回一個新的實例。
Microsoft.Extensions.DependencyInjection.ServiceLifetime
public enum ServiceLifetime{
Singleton,
Scoped,
Transient
}
服務的生命周期在注冊服務的時候確定。在使用的時候,直接獲取實例,不再指定服務的生命周期。微軟提供了多種擴展方法來便于在注冊服務時指定服務的生命周期。例如下面是通過泛型方式來指定單例模式的生命周期。
// 基于接口的注冊services.AddSingleton<IUserService, UserService>();
2.4 服務提供者 ServiceProvider
在需要使用服務對象實例的時候,不是從注冊服務的集合中獲取,而是需要通過服務提供者來獲取,這個服務提供者顯然需要來自注冊服務的集合。服務提供者的接口定義為?IServiceProvider,它是 .net 的基礎定義之一,不是在該 DI 框架中定義的。
public interface IServiceProvider{
object GetService(Type serviceType);
}
DI 中的?ServiceCollectionContainerBuilderExtensions?擴展了?IServiceCollection,提供了獲得這個服務提供者 ServiceProvider 的支持。
public static ServiceProvider BuildServiceProvider(this IServiceCollection services){
return BuildServiceProvider(services, ServiceProviderOptions.Default);
}
所以,我們通常使用該方法來獲取并使用它。
// 創建注冊服務的容器IServiceCollection services = new ServiceCollection ();
// 注冊服務,這里指定了單例
services.AddSingleton<IUserService, UserService>();
// 通過容器獲得服務提供者
IServiceProvider provider = services.BuildServiceProvider ();
2.5 獲取服務對象實例
通過服務提供者來手動獲取服務對象實例。通過注冊的服務類型,直接調用?GetService?方法即可。
例如,前面我們注冊了服務類型?IUserService?的實現類型是?UserService?,那么,可以通過此類型來獲取實際實現該接口的對象實例。
// 創建注冊服務的容器IServiceCollection services = new ServiceCollection ();
// 注冊服務,這里指定了單例
services.AddSingleton<IUserService, UserService>();
// 通過容器獲得服務提供者
IServiceProvider provider = services.BuildServiceProvider ();
// 通過接口獲取服務對象實例
IUserService instance = provider.GetService<IUserService> ();
看起來,更加復雜了。在實際使用中,我們很少使用這樣的方式來使用 DI,后面我們再深入討論具體的使用過程。
原文鏈接:https://www.cnblogs.com/haogj/p/11370314.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結
以上是生活随笔為你收集整理的依赖注入在 dotnet core 中实现与使用:1 基本概念的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core on K8S深
- 下一篇: 软件设计的第一性原理:结构化抽象