Asp.Net Core IOC模式

146 阅读5分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路

本文禁止转载,如需转载请注明出处。

Asp.Net Core IOC模式

IoC名词解释

IoC全名为Inverse of Control,含义为"控制倒置",针对软件设计行业来说,IoC所谓的控制其实是"针对流程的控制".控制权原来在应用程序,应用程序可以根据自己的场景定义流程,而IoC原则主张流程的控制权转移到框架中,由框架来定义流程的步骤,我们可以通过下图来说明控制权的转移过程.

IOC1.jpg

流程定制

我们采用IoC实现了流程控制从应用程序向框架自身的反转,但是这个被反转的仅仅是一个泛化的流程,任何一个具体的应用都可能需要对组成该流程的某些环节进行定制.作为一个Web框架,用户认证功能是必备的,但是框架自身不能限制于某一种或者几种固定的认证方式,应该通过扩展的方式让用户可以自由地定制任意的认证模式。

我们可以说得更加宽泛点。如下图所示我们将一个泛化的工作流程(A=>B=>C)被定义在框架之中,建立在该框架的两个应用需要对组成这个流程的某些环节进行定制。比如步骤A和C可以被App1重用,但是步骤B却需要被定制(B1),App2则重用步骤A和B,但是需要按照自己的方式处理步骤C。

IOC2.jpg

Asp.NET Core DI框架概览

毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动时构建请求处理管道的过程中,以及利用该管道处理每个请求过程中使用到的服务对象均来源于DI容器.该DI容器不仅为ASP.NET Core框架提供必要的服务,同时作为了应用的服务提供者,依赖注入已经成为了ASP.NET Core应用基本的编程模式.

服务的注册与消费

ASP.NET Core中的依赖注入框架中,我们添加的服务注册被保存到通过 IServiceCollection 接口表示的集合之中,基于这个集合创建的DI容器体现为 IServiceProvider.

DI框架提供了生产实例的三种生命周期模式,分别为:

  • Singleton:整个应用程序生命周期内单例
  • Scoped:单次服务请求内的单例
  • Transient:每次获取均会产生新的对象

DI框架注册服务时通常会使用三种方式,分别为:

  • 指定注册非服务类型和实现类型.
  • 指定一个现有的服务实例.
  • 指定一个创建服务实例的委托对象.

当我们在进行服务注册时,可以为同一类型添加一个服务注册,也可以添加多个,实际上添加的所有服务注册都是有效的,在完成服务注册之后,我们调用IServiceCollection接口的扩展方法 BuildServiceProvider创建出代表DI容器的IServiceProvider对象,并利用它调用后者的GetService方法来提供相应的服务实例,总时会返回一个服务实例,这里采用了"后来居上"的原则,即总是采用最近添加的服务注册来创建服务实例.如果我们调用另一个扩展方法GetServices,它将利用返回所有服务注册提供的服务实例.如下所示的代码片段.

DI1.jpg

生命周期管理

IServiceProvider之间的层次结构造就了三种不同的生命周期模式:由于Singleton服务实例保存在作为根容器的IServiceProvider对象上,所以它能够在多个同根IServiceProvider对象之间提供真正的单例保证。Scoped服务实例被保存在当前IServiceProvider上,所以它只能在当前IServiceProvider对象的“服务范围”保证的单例的。没有实现IDisposable接口的Transient服务则采用“即用即取,用后即弃”的策略。

接下来我们通过简单的实例来演示三种不同生命周期模式的差异。在如下所示的代码片段中我们创建了一个ServiceCollection对象并针对接口IFoo、IBar和IBaz注册了对应的服务,它们采用的生命周期模式分别为Transient、Scoped和Singleton。在利用ServiceCollection创建出代表DI容器的IServiceProvider对象之后,我们调用其CreateScope方法创建了两个所谓的“服务范围”,后者的ServiceProvider属性返回一个新的IServiceProvider对象,它实际上是当前IServiceProvider对象的子容器。我们最后利用作为子容器的IServiceProvider对象来提供相应的服务实例。

//根节点的ServiceProvider
var root = new ServiceCollection()
        .AddTransient<IFoo, Foo>()
        .AddScoped<IBar>(_ => new Bar())
        .AddSingleton<IBaz, Baz>()
        .BuildServiceProvider();

//子节点的ServiceProvider
var provider1 = root.CreateScope().ServiceProvider;
var provider2 = root.CreateScope().ServiceProvider;

void GetServices<TService>(IServiceProvider provider)
{
    provider.GetService<TService>();
    provider.GetService<TService>();
}

GetServices<IFoo>(provider1);
GetServices<IBar>(provider1);
GetServices<IBaz>(provider1);
Console.WriteLine();
GetServices<IFoo>(provider2);
GetServices<IBar>(provider2);
GetServices<IBaz>(provider2);

输出:Foo Foo Bar Baz
     Foo Foo Bar

总结

一方面IoC对框架的流程进行了封装,消费者只需要执行,框架利用引擎驱动整个流程的运转,应用程序无须关心该工作流程的细节,只需要启动引擎即可.另一方面,框架会提供一系列的扩展点,应用程序则通过定义扩展的方式实现对流程某个环节的定制,在引擎启动前,应用程序将所需的扩展注册到框架中,一旦引擎被正常启动,这些注册的扩展会自动参与到整个流程的执行过程中.这样就实现了框架对流程的高度复用和扩展.

参考

大内老A