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