.NET6最通俗易懂的依赖注入之批量注册

这篇文章是 ASP.NET 6 依赖注入系列文章的第 4 篇。

在上一篇文章中,我们讨论了依赖注入的服务注册与注入方式的内容。

接下来,在这篇文章中,我们将继续了解服务的批量注册与服务定位模式。.

Scrutor

虽然 .NET 中的依赖注入很好用,但在功能性上还是难以满足很多场景的。

比如我们在注册服务的时候,都是一项项的注册。

如果应用中,有大量的服务需要注册,逐项注册的方式就会显得很繁琐, 还很可能会有遗漏。

想要解决这个问题,我们可以使用第三方的依赖注入框架,比如 Autofac。

但是,Autofac 功能又过于强大,其中大部分的功能我们可能并不需要。

我们想要的仅仅是能够解决逐项注册服务的问题,那么还有一个轻量级的选择,就是使用一个名为 Scrutor的 .NET 依赖注入扩展库。

它可以很简单地添加到现有的 ASP.NET 应用程序中,我们只需要在项目中通过 Nuget 安装:

Install-Package Scrutor

除此之外,不需要编写任何额外的集成代码,因为它仅仅是对 .NET 依赖注入系统服务注册功能上的扩展。

Scrutor 相对于第三方依赖注入框架的好处就是,它更加轻量和便捷。

由于它只是扩展库,所以即便是 .NET 依赖注入系统在未来的更新中有所变化,Scrutor 受的影响也不会太大。

装配扫描

Scrutor 有两个针对服务集合ServiceCollection类的扩展方法:ScanDecorate

  • Scan方法用于扫描指定程序集,并按指定规则进行批量注册;

  • Decorate方法用于装饰已注册服务。

这里我们主要是用它的Scan方法,它的基本使用方式如下示例:

services.Scan(
    scan => scan              
    // 扫描 Program 类所在的程序集              
    .FromAssemblyOf<Program>()              
    // 筛选需要注册的类型     
    .AddClasses(classes => classes.Where(
        t => t.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase)))
    // 暴露注册类型的接口为服务标识     
    .AsImplementedInterfaces()              
    // 指定生命周期模式为 Scoped              
    .WithScopedLifetime());

Scan方法的参数是一个配置委托,在这个委托中一般需要定义四个东西:

  1. 选择程序集,选择要注册的服务类型在哪个程序集中,也就是要扫描哪个程序集,常用的有:

    • FromAssemblyOf<>FromAssembliesOf 扫描包含所提供的类型的程序集;
    • FromCallingAssembly,扫描调用该方法的程序集;
    • FromExecutingAssembly,扫描当前执行的程序集;
    • FromEntryAssembly ,扫描入口程序集。
  2. 筛选注册类,筛选注册扫描程序集中的类型:

    • AddClasses()添加所有公共的非抽象类;
    • AddClasses(publicOnly)添加所有非抽象类,设置 publicOnly=false可以添加 internal/private修饰的类;
    • AddClass(predicate) 添加符合筛选条件的类;
    • AddClasses(predicate, publicOnly) 前面两种方法的结合。
  3. 暴露服务标识,注册类型应该以何种方式作为标识暴露:

    • AsImplementedInterface(),暴露实现类所有的接口;

    • AsSelf(),有时我们注册的类是没有实现接口的,所以也可以用自身作为标识暴露;

    • AsMatchingInterface(),大多数情况下,接口和实现类都会根据IClass/Class这样的习惯来命名,因此当我们需要暴露匹配类型名称的接口时,就可以使用该方法;

    • As<> 可以自由选择任何类型作为标识进行暴露。

  4. 生命周期,注册服务使用的生命周期模式:

    • WithTransientLifetime(),瞬时生命周期;
    • WithScopedLifetime(),作用域生命周期;
    • WithSingletonLifetime(),单例生命周期模式。

重复注册策略

有时候可能会出现重复注册的情况,也就是同一个服务,有多个不同的实现类。

因此注册过程有先后,所以这个时候可以利用 Scrutor 提供的重复注册处理策略:Append 、SkipThrowReplace

  • Append 叠加注册,这是默认的策略,也就是所有实现类都注册,但默认情况只有最先注册的才会生效。

  • Skip 跳过重复的注册服务,已注册的服务,不再为其注册其它实现类。

  • Throw 抛弃已注册的接口,已注册的服务实现类将被抛弃,而是为该服务注册新的实现类。

  • Replace 替换策略,它有三种替换方式:替换重复的实现类、替换重复的接口、替换任意重复。

services.Scan(
    scan => scan              
    .FromAssemblyOf<Startup>()                                  
    .AddClasses()              
    // 重复注册处理策略              
    .UsingRegistrationStrategy(RegistrationStrategy.Skip));