在.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. 【Java面试】什么是幂等?如何解决幂等性问题?

    一个在传统行业工作了7年的粉丝私信我. 他最近去很多互联网公司面试,遇到的很多技术和概念都没听过. 其中就有一道题是:"什么是幂等.如何解决幂等性问题"? 他说,这个概念听都没听过 ...

  2. Servlet的本质

    简介:Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层. 功能:使 ...

  3. 前端2CSS2

    内容概要 伪元素选择器 选择器优先级 字体样式 文字属性 背景属性 display属性 边框属性 盒子模型 浮动(重要) 解决浮动造成的影响 内容详情 伪元素选择器 """ ...

  4. Tensorboard SummaryWriter()

    import torch import torch.nn as nn import torch.nn.functional as F import torchvision import torchvi ...

  5. ElasticSearch6.4.2

    做一个简单的API记录 1.依赖为6.4.2  比较老的版本 2.指定ES集群,可接多个Put(); Setting setting=Setting.builder().put("clust ...

  6. Puppeteer学习笔记 (1)- 什么是Puppeteer

    本文链接:https://www.cnblogs.com/hchengmx/p/11006263.html 1. phantomjs介绍 在介绍puppeteer之前必须介绍一下phantomjs,p ...

  7. numpy中的np.round()取整的功能和注意

    numpy中的np.round()取整的功能和注意 功能 np.round() 是对浮点数取整的一个函数,一般的形式为 np.round(a, b),其中a为待取整的浮点数,b为保留的小数点的位数 注 ...

  8. 聊聊C#中的composite模式

    写在前面 Composite组合模式属于设计模式中比较热门的一个,相信大家对它一定不像对访问者模式那么陌生,毕竟谁又没有遇到过树形结构呢.不过所谓温故而知新,我们还是从一个例子出发,起底一下这个模式吧 ...

  9. 关于使用koa实现线上 https服务

    var https=require("https");//https服务var fs= require("fs");var Koa = require('koa ...

  10. LVGL库入门教程 - 颜色和图像

    颜色 构造颜色 在 LVGL 中,颜色以结构 lv_color_t 表示.在最开始移植整个工程时,曾经在 lv_conf.h 中修改过颜色深度: /*Color depth: 1 (1 byte pe ...