探索 .NET Core 依赖注入的 IServiceCollection

如果您使用了.NET Core,则很可能已使用Microsoft.Extensions.DependencyInjection中的内置依赖项注入容器,在本文中,我想更深入地了解Microsoft Dependency Injection(DI)容器中的 IServiceCollection。

什么是依赖注入(DI)和DI容器?

Microsoft依赖项注入容器只是一组类,它们组合到一个代码库中,这个库会自动创建并管理程序中需要的对象。

我们先看下面的代码:

public class ClassA
{public void DoWork() {var b = new ClassB();b.DoStuff();}
}public class ClassB
{public void DoStuff(){// ...}
}

ClassA直接依赖ClassB,并且在它的DoWork方法中,new了一个ClassB,然后调用了ClassB的DoStuff方法。

我们改写一下代码看看:

public class ClassA
{private readonly ClassB _dependency;public ClassA(ClassB classB) => _dependency = classB;public void DoWork() => _dependency.DoStuff();
}public class ClassB : IThing
{public void DoStuff(){// ...}
}

首先,我加了一个构造函数,并且指定了ClassA依赖的类型,调用构造函数时,必须提供ClassB的实例, 在ClassA的内部,我们不会去new一个ClassB,ClassB完全是由外部传入的,这里就是控制反转(IoC)。

进一步改进代码:

public interface IThing
{public void DoStuff();
}public class ClassA
{private readonly IThing _dependency;public ClassA(IThing thing) => _dependency = thing;public void DoWork() => _dependency.DoStuff();
}public class ClassB : IThing
{public void DoStuff(){// ...}
}

加了一个接口IThing,现在,我们已经应用了SOLID的依赖倒置原则,我们不再依赖具体的实现,相反,我们依赖于IThing抽象,在构造函数中,只需要传入IThing的实现就好了。

然后在我们的代码中,可以这样用:

class Program
{static void Main(string[] args){IThing thing = new ClassB();ClassA classA = new ClassA(thing);classA.DoWork();}
}

我们手动new了一个ClassB,它实现了IThing接口,然后创建ClassA的时候,直接把thing传入构造函数中。

上面的代码演示,我们只处理了ClassA和ClassB的依赖注入关系,但是在实际中呢,我们代码中有很多类型,然后有各种各样的依赖关系。

这个时候我们就需要一个DI容器,我们对容器进行配置,然它知道什么类型,然后负责自动创建并管理对象(通常称为服务)。

注册服务

通常, Microsoft DI 容器需要在Startup类中配置,在这里,您可以使用ConfigureServices方法向容器注册服务,在应用程序托管生命周期的早期,将调用ConfigureServices方法,它有一个参数IServiceCollection,这个参数在初始化应用程序时传入。

public class Startup
{public void ConfigureServices(IServiceCollection services){// 注册服务}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){}
}

为了尽可能的简单,我们也可以在控制台中使用 Microsoft DependencyInjection。

创建控制台程序后,我们首先在项目中引入Microsoft.Extensions.DependencyInjection

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net5.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" /></ItemGroup></Project>

现在我们开始注册我们的服务,但是我们需要一个IServiceCollection,让我们看一下IServiceCollection的定义。

public interface IServiceCollection : IList<ServiceDescriptor>
{
}

IServiceCollection没有定义其任何成员,而是从IList<ServiceDescriptor>派生。

Microsoft.Extensions.DepenencyInjection程序包里面,它有一个默认的实现:ServiceCollection。

public class ServiceCollection : IServiceCollection
{private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();public int Count => _descriptors.Count;public bool IsReadOnly => false;public ServiceDescriptor this[int index]{get{return _descriptors[index];}set{_descriptors[index] = value;}}// ...
}

它有一个私有的List集合:_descriptors,里面是ServiceDescriptor。

让我们从创建一个ServiceCollection,然后注册两个服务。

static void Main(string[] args)
{var serviceCollection = new ServiceCollection();serviceCollection.AddSingleton<ClassA>();serviceCollection.AddSingleton<IThing, ClassB>();Console.WriteLine("Done");
}

在前面的代码中,我们已经使用AddSingleton方法注册了两个服务,这不是IServiceCollection接口定义的方法,也不在ServiceCollection上,这是IServiceCollection的扩展方法,这个方法在ServiceCollectionServiceExtensions的扩展类中,接下来,我会介绍这个方法是如何注册服务的,不过这之前,我们首先回顾下服务生命周期的概念。

服务生命周期

在Microsoft依赖项注入框架中,我们可以使用三种生命周期注册服务,分别是单例(Singleton)、瞬时(Transient)、作用域(Scoped),在上面的代码中,我使用了AddSingleton()来注册服务。

使用Singleton服务的优点是我们不会创建多个服务实例,只会创建一个实例,保存到DI容器中,直到程序退出,这不仅效率高,而且性能高,但是有一个要注意的点,如果在多线程中使用了Singleton,要考虑线程安全的问题,保证它不会有冲突。

瞬时(Transient)和单例(Singleton)模式是相反的,每次使用时,DI容器都是创建一个新的实例。

作用域(Scoped),在一个作用域内,会使用同一个实例,像EF Core的DbContext上下文就被注册为作用域服务。

我们注册服务时会发生什么?

在上面的代码中,我已经注册了两个单例服务。

serviceCollection.AddSingleton<ClassA>();
serviceCollection.AddSingleton<IThing, ClassB>();

这是最终的AddSingleton方法:

public static IServiceCollection AddSingleton(this IServiceCollection services,Type serviceType,Type implementationType)
{if (services == null){throw new ArgumentNullException(nameof(services));}if (serviceType == null){throw new ArgumentNullException(nameof(serviceType));}if (implementationType == null){throw new ArgumentNullException(nameof(implementationType));}return Add(services, serviceType, implementationType, ServiceLifetime.Singleton);
}

我们可以看到AddSingleton方法调用了私有的Add方法,并且传入了一个生命周期的枚举值ServiceLifetime.Singleton

让我们看一下Add方法的工作原理:

private static IServiceCollection Add(IServiceCollection collection,Type serviceType,Type implementationType,ServiceLifetime lifetime)
{var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);collection.Add(descriptor);return collection;
}

它创建一个新的ServiceDescriptor实例,传入服务类型,实现类型(可能与服务类型相同)和生命周期,然后调用Add方法添加到列表中。

之前,我们了解到IServiceCollection本质上是包装了List <ServiceDescriptor>, ServiceDescriptor类很简单,代表一个注册的服务,包括其服务类型,实现类型和生命周期。

实例注册

我们也可以手动new一个实例,然后传入到AddSingleton()方法中:

var myInstance = new ClassB();
serviceCollection.AddSingleton<IThing>(myInstance);

使用 ServiceDescriptor

我们还可以手动定义一个ServiceDescriptor,然后直接添加到IServiceCollection中。

var descriptor = new ServiceDescriptor(typeof(IThing), typeof(ClassB), ServiceLifetime.Singleton);
serviceCollection.Add(descriptor);

总结

在本文中,介绍了.NET中的DI的一些核心知识,可以直接创建ServiceCollection来使用Microsoft DI框架,了解了IServiceCollection上的AddSingleton扩展方法是如何工作,以及它们最终创建了一个ServiceDescriptor,然后添加到一个ServiceCollection包装的List集合中。

原文链接: https://www.stevejgordon.co.uk/aspnet-core-dependency-injection-what-is-the-iservicecollection

欢迎扫码关注我们的公众号 【全球技术精选】,专注国外优秀博客的翻译和开源项目分享,也可以添加QQ群 897216102

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/304689.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

dev可以运行mysql文件夹_Linux查看mysql 安装路径和运行路径

一、查看文件安装路径由于软件安装的地方不止一个地方&#xff0c;所有先说查看文件安装的所有路径(地址)。这里以mysql为例。比如说我安装了mysql,但是不知道文件都安装在哪些地方、放在哪些文件夹里&#xff0c;可以用下面的命令查看所有的文件路径在终端输入&#xff1a;whe…

看完这13张图,不得不佩服还是外国人会玩人工智能

对于程序员来说&#xff0c;机器学习领域无疑充满着巨大的诱惑和挑战&#xff0c;很多人对里面复杂的概念和算法头疼不已&#xff0c;那么&#xff0c;有没有一套对新手既友好又明了&#xff0c;对老手能加深印象&#xff0c;不断复习的学习办法呢&#xff1f;有&#xff0c;今…

python通过封装可以实现代码复用_Python学习笔记(五)函数和代码复用

函数能提高应用的模块性&#xff0c;和代码的重复利用率。在很多高级语言中&#xff0c;都可以使用函数实现多种功能。在之前的学习中&#xff0c;相信你已经知道Python提供了许多内建函数&#xff0c;比如print()。同样&#xff0c;你也可以自己创建函数&#xff0c;这被叫做用…

实用的网络命令汇总

通过ping检测网络故障的典型次序 正常情况下&#xff0c;当你使用ping命令来查找问题所在或检验网络运行情况时&#xff0c;你需要使用许多ping命令&#xff0c;如果所有都运行正确&#xff0c;你就可以相信基本的连通性和配置参数没有问题&#xff1b;如果某些ping命令出现运行…

EFCore查缺补漏(二):查询

相关文章&#xff1a; EFCore查缺补漏第 20 轮 TechEmpower 评测结果出炉了&#xff0c;ASP.NET Core 的 Plaintext 成绩名列前茅&#xff0c;带着 EFCore 的测试却在 Single query / Multiple queries / Fortunes 中落了下风&#xff0c;成绩远不如 dapper&#xff0c;更不如直…

如何在mysql中打开mongodb_图解:如何从MySQL移植到MongoDB

【IT168 技术】下图是使用 SQL 和 MongoDB 的对应信息图表&#xff0c;点击图片查看大图&#xff1a;MongoDB是一个介于关系数据库和非关系数据库之间的产品&#xff0c;是非关系数据库当中功能最丰富&#xff0c;最像关系数据库的。他支持的数据结构非常松散&#xff0c;是类似…

仿真模拟,需要注意这几点!

周日晚上的直播甚是精彩先是潘同学分享了获奖论文和解题技巧接着是董同学分享了论文的写作小技巧po几张截图让大家感受下此时此刻可能会有不少童鞋正在为错过直播而懊悔不用担心超模君还准备了一份豪华大礼本周超模君特意邀请到今年美赛D题特等奖获奖者谢挺同学&#xff08;获奖…

mfc从mysql中读取数据类型_在MFC中使用SQlite数据库读取数据

本人在数据库里面用回调函数来处理读取函数的返回值&#xff0c;回调函数必须使用static类型才可以,这样处理起返回的数据变得非常的麻烦&#xff0c;很难处理结果集。后来从网上找了一个预编译的例子&#xff0c;放在网上和网友分享。例子成功的调试通过了sqlite3 *db 0;sqli…

php 结构体_php基础知识集合

微信公众号&#xff1a;PHP在线源码PHP 独特的语法混合了 C、Java、Perl 以及 PHP 自创新的语法编译器编译器就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序一个现代编译器的主要工作流程&#xff1a;源代码 (source code) → 预处理器 (prepr…

Kubernetes 凭什么成了云原生应用底座?

微软开源的 云原生应用开发的框架 Dapr 发布了1.0 版本《Dapr 正式发布 1.0》&#xff0c;云原生应用开发更加简单容易&#xff0c;Dapr 的容器编排环境也是用的Kubernetes。过去几年&#xff0c;以 Docker、Kubernetes 为代表的容器技术已发展为一项通用技术&#xff0c;BAT、…

【汇总推荐】深度学习、自然语言处理干货笔记汇总

今天这篇文章对十一月份推送的干货笔记&#xff0c;做一个总结&#xff0c;大家可以进行分类查找&#xff0c;希望对大家有所帮助。人工智能实战技巧与学习方法系列免费公开课【重磅福利】人工智能实战技能与学习方法系列公开课免费分享深度学习干货文章【直观理解】一文搞懂RN…

计算机网络四级考试

计算机网络四级考试         第一章网络系统结构与设计的基本原则7月15日  8月6日          第二章中小型网络系统总体规划与设计方法7月17日  8月8日          第三章IP地址规划和设计方法7月19日  8月10日          第四章路由…

mongodb时间范围查询少8个小时_为何要对开源mongodb数据库内核做二次开发

关于作者前滴滴出行技术专家&#xff0c;现任OPPO文档数据库mongodb负责人&#xff0c;负责oppo千万级峰值TPS/十万亿级数据量文档数据库mongodb内核研发及运维工作&#xff0c;一直专注于分布式缓存、高性能服务端、数据库、中间件等相关研发。后续持续分享《MongoDB内核源码设…

谷歌微软高通反对英伟达收购ARM 值得国人深思

日前&#xff0c;外媒报道全球一些顶尖科技公司正在向美国反垄断监管机构表达反对英伟达公司收购ARM&#xff0c;认为该交易将损害对其业务至关重要的行业领域的竞争。谷歌、微软和高通公司等公司都对这笔价值400亿美元的交易感到担忧&#xff0c;并要求反垄断官员进行干预。谷…

爬虫 404 try_和我一起学爬虫

前几天就想写一个爬虫系列的文章&#xff0c;因为比较忙所以没有写&#xff08;还不是因为懒&#xff09;&#xff0c;趁着现在屋里比较的凉爽&#xff0c;心也比较的静&#xff0c;总结下目前遇到的一些爬虫知识&#xff0c;本系列将从简单的爬虫开始说起&#xff0c;以后会逐…

女神一秒变路人!腾讯研究AI卸妆效果算法出品“一键卸妆”功能

综合自&#xff1a;快科技、腾讯国际计算机视觉大会&#xff08;ICCV&#xff09;于 10 月 22 日到 29 日在意大利威尼斯举办&#xff0c;会上&#xff0c;腾讯优图团队带来了一个让所有女生恨得牙痒痒的功能——“一键卸妆”。所谓道高一尺魔高一丈&#xff0c;美图秀秀造福了…

perl对文件和目录进行操作

对文件和目录进行操作opendir 目录句柄&#xff0c;“目录”&#xff1b;for $a(readdir 目录句柄){printer $a}areaddir HOME;打印目录&#xff0c;没有排序。查找递归目录用File::Find删除文件&#xff1a;&#xff08;只是删掉连接&#xff0c;数据应该还没有删掉。我是这样…

二进制序列化

在计算机世界&#xff0c;万物皆01二进制&#xff0c;包括各种各样的文件格式和网络协议&#xff0c;二进制格式最为常见&#xff01;NewLife.Core 内置了完整的二进制序列化框架 Binary&#xff0c;经过十多年洗礼&#xff0c;发展到了第三代支持Handler处理器扩展。Binary的同…

python搭建项目结构_Django搭建项目实战与避坑细节详解

Django 开发项目是很快的&#xff0c;有多快&#xff1f;看完本篇文章&#xff0c;你就知道了。安装 Django前提条件&#xff1a;已安装 Python。Django 使用 pip 命令直接就可以安装&#xff1a;pip install django如果安装失败&#xff0c;很可能是因为网络连接超时了&#x…

matlab基于ssd的角点匹配_基于关键点的目标检测

0 1前言&#xff1a;基于锚点的目标检测方法在基于关键点(key points)的目标检测方法出现之前&#xff0c;主流目标检测方法一般先设置一些预先定义好的 锚点 (anchor boxes)。 作为预测物体框的参考&#xff0c;神经网络只需要预测实际的物体框相对于这些锚点的偏移。 这样模型…