依赖注入在 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> ();
相关文章
相关标签/搜索