依賴注入在 dotnet core 中實現與使用:1 基本概念

關於 Microsoft Extension: DependencyInjection 的介紹已經不少,可是多數偏重於實現原理和一些特定的實現場景。做爲 dotnet core 的核心基石,這裏準備全面介紹它的概念、原理和使用。git

這裏首先介紹概念部分。github

1. 概念

該項目在 GitHub 的地址:https://github.com/aspnet/Extensions/tree/master/src/DependencyInjectionweb

Microsoft.Extensions.DependencyInjection 是微軟對依賴倒置原則的實現。做爲 ASP.NET Core 的基石,DependencyInjection 貫穿了整個項目的方方面面,掌握它的使用方式和原理,不只對理解 ASP.NET Core 有重要意義,也有助於將它運用到其它項目的開發中,幫助提供項目開發的效率和質量。編程

1.1 問題的場景

在軟件開發中,項目一般有多個不一樣的模塊組成,模塊之間存在依賴關係。例如,咱們考慮一個簡化的場景,咱們有 3 個關於用戶的類:設計模式

  1. AccountController,提供用戶交互界面框架

  2. UserService,提供用戶管理的業務邏輯ide

  3. 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();
    }
}

 

這致使了兩個類之間的緊耦合,AccountControllerUserService 被綁定到一塊兒, 在每次建立 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 ();

 在 ASP.NET Core MVC 中,你可能已經見過它了,不須要你來建立,系統已經幫你作了。

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IRepository, MemoryRepository>();
    services.AddMvc();
}

 

2.2 服務 Service

在 DI 語境中,服務特指經過 DI 容器管理的對象實例。這個服務並不必定被稱爲 **Service,而是能夠是任何由 DI 所管理的對象,只是在 DI 這個語境下,咱們將其統稱爲服務。

服務是咱們本身定義的,例如前面提到的 AccountControllerUserService 等等。

咱們經過 DI 來得到服務對象實例,管理服務對象的生命週期,對於存在複雜依賴關係的對象, DI 還負責管理這些實例之間的依賴關係。

服務必須首先註冊在 DI 中才能使用,可是,註冊前須要首先考慮和決定服務的生命週期。

2.3 服務的生命週期

服務對象實例有着不一樣類型的生命週期。有些對象的生命週期與應用程序相同,在應用程序啓動時建立,在應用程序退出時才須要釋放。例如咱們的數據訪問對象實例。有些對象僅僅在當前方法中使用,在方法調用結束以後就應該銷燬。服務的生命週期管理用來管理這些需求。

DI 支持三種類型的生命週期:

  1. Singleton,單例,在當前應用程序環境下只有一個實例。例如數據訪問服務對象實例。

  2. Scoped,限定範圍,一旦退出此範圍,在此範圍內的服務對象都須要銷燬。例如 Web 開發中的請求對象實例。

  3. 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,後面咱們再深刻討論具體的使用過程。

 

2.6 構造函數注入

DI 支持構造函數注入。

定義 IUserRepository 接口,並實現 UserRepository

public interface IUserRepository {
}
​
public class UserReposotory: IUserRepository { 
}

 

UserService 經過構造函數依賴 IUserRepository

public class UserService : IUserService {
    private readonly IUserRepository _userRepository;
    public UserService(IUserRepository userRepository) {
        this._userRepository = userRepository;
    }
}

 

在經過 DI 得到 UserService 實例的時候,DI 幫助實例化其所依賴的 UserRepository 實例。

UserService 的定義中,咱們只須要經過構造函數聲明所需的依賴便可。

IServiceCollection services = new ServiceCollection ();
​
// 基於接口的註冊
services.AddSingleton<IUserService, UserService>();
services.AddSingleton<IUserRepository, UserReposotory>();
​
IServiceProvider provider = services.BuildServiceProvider ();
IUserService instance = provider.GetService<IUserService> ();
相關文章
相關標籤/搜索