在.NET Core的依赖注入框架中,服务注册的信息将会被封装成ServiceDescriptor对象,而这些对象都会存储在IServiceCollection接口类型表示的集合中,另外,IServiceCollection接口类型默认使用的实现类型为ServiceCollection。这样来看,实际上服务注册这回事,它就是一个将创建的ServiceDescriptor对象添加到IServiceCollection接口类型集合中的过程。

通常一个项目,都是由大量的对象相互协作而构建成的应用程序。所以对于使用依赖注入框架的应用程序而言,需要进行大量的服务注册,并且其中会存在不同形式的注册需求。.NET Core为此提供了大量不同形式的服务注册方法,为了方便使用,它将这些方法都定义为了IServiceCollection接口的扩展方法,这些扩展方法主要分布在

ServiceCollectionServiceExtensions和ServiceCollectionDescriptorExtensions这两个类中。这两个类型都可以实现服务的注册,但是具有如下不同的侧重点:

  1. ServiceCollectionServiceExtensions:提供以生命周期模式命名,无需指定生命周期参数的注册方法;
  2. ServiceCollectionDescriptorExtensions:主要以ServiceDescriptor对作为参数进行服务注册,并包含避免同一类型服务重复注册的方法和维护IServiceCollection集合的方法;

个人觉得前者更加简单易用,后者则配置灵活且功能丰富,下面将围绕这两个类型中的服务注册方法进行介绍。


1.ServiceCollectionServiceExtensions

该类型中关于服务注册方法的命名,通常是以固定字符“Add”作为前缀,“Add”后面则是会加上不同的生命周期模式名称。该类型中的注册方法也属于实际开发场景中较为常用的一种,因为使用这种方式的好处是:无需在服务注册时传入一个具体的生命周期,而是根据服务对生命周期模式的需求,选择与模式名称相同的方法即可。

例如,需要创建生命周期模式为Singleton的服务,那么与之对应的注册方法就是AddSingleton。并且可以根据AddSingleton方法的重载选择任意一种提供服务实例的方式。一般最常用的重载形式是,使用对应泛型方法传入服务类型和实现类型,例如下面的使用方式:

1    var collection = new ServiceCollection();
2 collection.AddSingleton<IFooBar, FooBar>();

该类型的注册方法主要都是根据3种生命周期模式而设计,并结合ServiceDescriptor类型中3种提供服务实例的方式进行扩展,从而延申出了很多重载形式,更加方便的为应用程序提供服务注册。如果想要具体了解每个生命周期模式的不同重载可以参考官方说明:


2.ServiceCollectionDescriptorExtensions

相比ServiceCollectionServiceExtensions类型中的方法而言,虽然ServiceCollectionDescriptorExtensions类型从名称上只有一词之差,但是它的方法涉及范围更加丰富。该类型不仅具备服务的注册,还具备了对服务注册信息的“增删改”,并且包含针对服务注册重复性判断的方法,接下来则针对该类型中的方法进行一个介绍。

2.1.Add

该方法需要我们将服务注册的信息创建为一个ServiceDescriptor对象,并将该对象作为参数传入Add方法,以此形式作为服务注册。该方法有两种重载形式,一种是添加单个ServiceDescriptor对象,另一种是可以添加多个ServiceDescriptor对象的集合,对于该方法的调用示例可以参考如下代码:

 1             //服务注册信息集合
2 var serviceCollection = new ServiceCollection();
3
4 //添加“单个”服务注册信息对象
5 ServiceDescriptor descriptor = new ServiceDescriptor(typeof(IFooBar),typeof(FooBar), ServiceLifetime.Singleton);
6 serviceCollection.Add(descriptor);
7
8 //添加“多个”服务注册信息对象
9 List<ServiceDescriptor> descriptorList = new List<ServiceDescriptor>()
10 {
11 new ServiceDescriptor(typeof(IAnimal),typeof(Dog), ServiceLifetime.Singleton),
12 new ServiceDescriptor(typeof(IEmployee),typeof(Programmer), ServiceLifetime.Singleton)
13 };
14 serviceCollection.Add(descriptorList);

2.2.服务重复注册

.NET Core依赖注入框架中支持对同一服务类型进行多次注册。因为后续介绍的某些方法会针对这种重复多次的注册方式有相应的处理逻辑,所以在介绍那些方法之前,首先来介绍下服务重复注册这个行为有哪些特点。

1.对于同一个服务类型进行多次注册,使用GetService获取实例时,只会获取一个最近注册的服务实例。例如下图的代码示例中,分别针对同一服务类型IAnimal进行了多次注册,其中实现类型包含:Dog、Cat、Pig,但最后获取的实例只有Pig一个。

2.对于同一个服务类型进行多次注册,如果要获取该服务类型注册的所有服务实例,则可以使用GetServices方法来获取。例如下图的代码示例中,分别针对同一服务类型Base进行了多次注册,在通过调用GetServices方法后,将服务类型Base注册的所有服务实例都存储到了一个集合中。

2.3.TryAdd形式的方法

之所以在在此处介绍的标题为TryAdd形式的方法,是因为除了TryAdd方法本身之外,还有以TryAdd作为方法名称前缀的方法,分别是:TryAdd{生命周期模式}和TryAddEnumerable,并且3个方法都是围绕同一个主题,即避免服务重复注册。它们在调用的时候都会去检查服务类型是否已经注册过,如果已经注册了,则放弃当前方法的注册。TryAdd方法本身和TryAdd{生命周期模式}处理的逻辑基本是一致的,TryAddEnumerable处理会略有不同,下面针对这3类TryAdd形式的方法进行逐一介绍。

TryAdd

该方法需要我们将服务注册的信息创建为一个ServiceDescriptor对象,类似于Add方法,只不过在Add的基础上加了服务重复注册的判断逻辑,使用方式如下图示例:

该示例执行的结果:最终只会获取到第一次注册的服务示例,因为当第二次调用TryAdd时就发现了已经存在相同服务类型的注册,此时它会放弃注册行为。

TryAdd{生命周期模式}

TryAdd{生命周期模式}类型的方法可以看作是TryAdd方法的一种延申形式,处理逻辑和TryAdd方法一致。但是使用起来更加的便捷,因为它是根据3种生命周期模式扩展而来的版本,使用该类型方法注册时无需指定生命周期模式参数,而是根据生命周期需求选择对应的方法名来决定生命周期模式。

以上是TryAdd{生命周期模式}类方法的使用示例,可以看出相比TryAdd方法而言,该方法更加方便,我们无需创建ServiceDescriptor对象和指定生命周期模式。该示例以Singleton模式为例,其他两种模式的调用与此一致,此处就不一一演示了。

TryAddEnumerable

对于TryAddEnumerable方法判断服务重复性的逻辑而言,不在是和前两者的方法一样:仅仅使用服务类型这一个条件来判断重复性。TryAddEnumerable方法在检查重复性时会同时考虑“服务类型”和“实现类型”。如果发现某个服务注册信息对象的“服务类型”和“实现类型”,与当前即将要注册服务信息的“服务类型”和“实现类型”相同时,TryAddEnumerable方法会放弃当前的注册,以避免出现对于这种情况的重复注册。下面基于这个逻辑通过代码示例来证明这一点。

对于上面的示例而言,都是针对同一服务类型使用TryAddEnumerable方法的注册。其中对于第三次进行的Dog的注册而言,此时的ServiceCollection服务注册集合中已经存在了一个相同服务类型的注册,并且已存在的这个服务注册的实现类型也与之相同,所以第三次进行的注册被TryAddEnumerable方法认定为一个重复性的注册,故没有添加到ServiceCollection集合中。而Cat注册时虽然已经存在了相同服务类型的IAnimal,但是没有服务类型和实现类型同时相同的Cat注册,即不满足TryAddEnumerable的重复性判断条件,所以该服务注册会被添加到ServiceCollection服务注册集合中。


3.维护性方法

.NET Core依赖注入框架对于服务注册的行为,实际上就是将ServiceDescriptor(服务注册信息描述对象)添加到IServiceCollection集合中的过程。所以对于一个集合而言除了用于服务注册的添加方法之外,还有一些维护性的方法,用于对IServiceCollection中的服务注册进行:删除、替换、清空等操作,这些方法的来源主要来自两点:

  1. 由IList<ServiceDescriptor>接口继承而来;
  2. ServiceCollectionDescriptorExtensions类定义的扩展方法;

下面通过代码示例对一些常用的方法进行演示,并通过注释对方法使用的细节进行强调:

 1            var serviceCollection = new ServiceCollection();
2 serviceCollection.AddSingleton<IAnimal, Dog>();
3 serviceCollection.AddSingleton<Base, Pig>();
4
5 //替换:将旧的ServiceDescriptor对象删除,在新增一个新的ServiceDescriptor对象
6 var catDescriptor = ServiceDescriptor.Scoped<IAnimal, Cat>();
7 serviceCollection.Replace(catDescriptor);
8
9 //删除:删除指定服务类型的注册
10 serviceCollection.RemoveAll<Base>();
11
12 //清空ServiceCollection集合中所有的服务注册信息对象
13 serviceCollection.Clear();

4.自动注册

.NET Core依赖注入框架虽然为我们提供了丰富的服务注册方法,但是对于大型的项目而言,服务注册将成为一种高频次且重复性的枯燥工作,那么对于这样的一种情形而言,有没有一种自动化的注册方式呢?答案是有的,就是借助使用一个第三方开源的NetCore.AutoRegisterDi组件来实现服务的自动注册。下面将通过一个示例来演示如何使用NetCore.AutoRegisterDi组件。

1.从NuGet下载NetCore.AutoRegisterDi

通过NuGet下载组件的方式有很多,你可以用不同的下载,本示例通过利用VS中可视化的NuGet界面进行下载。

2.建立特征

NetCore.AutoRegisterDi组件自动化注册的前提是,要求服务注册的实现类型具有某一种特征,该组件会基于这种特征查找出与该特征匹配的所有类型,然后对这些符合特征的类型进行服务注册,最终注入到依赖它的类型中。本示例中服务注册的实现类型所采用的特征是:所有服务实现类型名称都是以“Service”结尾。

 1 using System;
2
3 namespace DependencyInjectionDemo
4 {
5 //服务类型
6 public interface IComputer
7 {
8 string SayHi(); //打招呼
9 }
10
11 //服务实现类型
12 public class ComputerService : IComputer
13 {
14 public string SayHi()
15 {
16 return "你好,我是戴尔电脑。";
17 }
18 }
19
20 }

3.定义注入形式

本示例中是将注册的服务提供给MVC中的控制器对象,控制器对象对依赖的服务提供以构造函数形式的注入方式,如下代码所示。

 1  public class HomeController : Controller
2 {
3 private IComputer _computer;
4
5 public HomeController(IComputer computer)
6 {
7 _computer = computer;
8 }
9
10 public IActionResult Index()
11 {
12 ViewBag.Msg = _computer.SayHi();
13 return View();
14 }
15
16 }

4.配置自动注册

以ASP.NET Core的项目为例,我们需要在Startup类的ConfigureServices方法中添加如下的代码:

1            //获取服务实现类型所在的程序集,一般都在实体层
2 var modelAssembly = Assembly.Load("DependencyInjectionDemo");
3
4 //服务自动注册
5 services.RegisterAssemblyPublicNonGenericClasses(modelAssembly)
6 .Where(c => c.Name.EndsWith("Service"))
7 .AsPublicImplementedInterfaces(ServiceLifetime.Scoped);

其中RegisterAssemblyPublicNonGenericClasses方法用于获取某个程序集中的所有类型(在实际的项目中我们的服务实现类型一般都定义在实体层),在RegisterAssemblyPublicNonGenericClasses方法调用之后,根据为“服务实现类型”定义的特征来获取相应的类型,此处我们使用Where方法结合特征(以Service结尾的类)筛选出相应的类型。AsPublicImplementedInterfaces方法表示,将针对前面两个方法筛选出的所有公共实现类型,使用指定的生命周期模式结合服务类型进行服务注册。

在完成以上的步骤后,后续对于应用程序的服务注册,都将由NetCore.AutoRegisterDi组件自动完成,那么这样一来,对于后续使用依赖注入框架而言,我们仅仅只需要为依赖的服务定义构造函数的注入形式即可。


学习感悟:学习就像是烧开水,烧到30度、60度、90度,烧的次数在多,没有烧到100度沸点则都是白费。

ASP.NET Core依赖注入系统学习教程:关于服务注册使用到的方法的更多相关文章

  1. ASP.NET Core依赖注入系统学习教程:容器对构造函数选择的策略

    .NET Core的依赖注入容器之所以能够为应用程序提供服务实例,这都归功于ServiceDescriptor对象提供的服务注册信息.另外,在ServiceDescriptor对象中,还为容器准备了3 ...

  2. ASP.NET Core 依赖注入最佳实践与技巧

    ASP.NET Core 依赖注入最佳实践与技巧 原文地址:https://medium.com/volosoft/asp-net-core-dependency-injection-best-pra ...

  3. asp.net core 依赖注入几种常见情况

    先读一篇注入入门 全面理解 ASP.NET Core 依赖注入, 学习一下基本使用 然后学习一招, 不使用接口规范, 直接写功能类, 一般情况下可以用来做单例. 参考https://www.cnblo ...

  4. ASP.NET Core 依赖注入基本用法

    ASP.NET Core 依赖注入 ASP.NET Core从框架层对依赖注入提供支持.也就是说,如果你不了解依赖注入,将很难适应 ASP.NET Core的开发模式.本文将介绍依赖注入的基本概念,并 ...

  5. # ASP.NET Core依赖注入解读&使用Autofac替代实现

    标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...

  6. 实现BUG自动检测 - ASP.NET Core依赖注入

    我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评. 如果以后有时间的话,我打算写一个系列的[实现BUG自动检测 ...

  7. [译]ASP.NET Core依赖注入深入讨论

    原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...

  8. ASP.NET Core依赖注入——依赖注入最佳实践

    在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...

  9. 自动化CodeReview - ASP.NET Core依赖注入

    自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...

随机推荐

  1. 斯坦福NLP课程 | 第18讲 - 句法分析与树形递归神经网络

    作者:韩信子@ShowMeAI,路遥@ShowMeAI,奇异果@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www. ...

  2. MASA Auth - 权限设计

    权限术语 Subject:用户,用户组 Action:对Object的操作,如增删改查等 Object:权限作用的对象,也可以理解为资源 Effect:规则的作用,如允许,拒绝 Condition:生 ...

  3. PostgreSQL Array 数组类型与 FreeSql 打出一套【组合拳】

    前言 PostgreSQL 是世界公认的功能最强大的开源数据库,除了基础数据类型 int4/int8/varchar/numeric/timestamp 等数据类型,还支持 int4[]/int8[] ...

  4. 5. Docker compose

    把上图添加路径后,改成下图: 上图之后需要source /etc/profile #此命令重新加载环境变量文件. 在任意目录下输入docker-compose测试下,docker-compose是否安 ...

  5. 【摸鱼神器】UI库秒变LowCode工具——列表篇(二)维护json的小工具

    上一篇介绍了一下如何实现一个可以依赖 json 渲染的列表控件,既然需要 json 文件,那么要如何维护这个 json 文件就成了重点,如果没有好的维护方案的话,那么还不如直接用UI库. 所以需要我们 ...

  6. 【生成对抗网络学习 其三】BiGAN论文阅读笔记及其原理理解

    参考资料: 1.https://github.com/dragen1860/TensorFlow-2.x-Tutorials 2.<Adversarial Feature Learning> ...

  7. 全球共有多少MySQL实例在运行?这里有一份数据

    摘要 Shadowserver Foundation在5月31日发布了一份全网的MySQL扫描报告,共发现了暴露在公网的360万个MySQL实例.因为这份报告基数够大,而且信息也非常完整,从数据库专业 ...

  8. Citus 11 for Postgres 完全开源,可从任何节点查询(Citus 官方博客)

    Citus 11.0 来了! Citus 是一个 PostgreSQL 扩展,它为 PostgreSQL 添加了分布式数据库的超能力. 使用 Citus,您可以创建跨 PostgreSQL 节点集群透 ...

  9. JavaGUI——Java图形用户界面

    1.Java GUI 概述 GUI(Graphical User Interface,简称 GUI,图形用户界面)是指采用图形方式显示的计算机操作用户界面,与早期计算机使用的命令行界面相比,图形界面对 ...

  10. python小题目练习(十)

    题目:根据生日判断星座 需求:实现如下图所示结果 代码展示: """Author:mllContent:根据生日判断星座Date:2020-11-23"&quo ...