探索 .NET Core 依赖注入的 IServiceProvider

在上一篇文章中,我们学习了Microsoft.Extensions.DependencyInjection中的IServiceCollection,包括服务注册转换为ServiceDescriptors,然后添加到集合中。

探索 .NET Core 依赖注入的 IServiceCollection[1]

在本文中,我们会学习 IServiceProvider,了解它是什么,以及它是怎么创建出来的,我们将根据上一篇文章中创建的IServiceCollection来学习如何构建IServiceProvider。

什么是 IServiceProvider?

IServiceProvider会根据程序的要求在运行时解析服务类型的实例,ServiceProvider来保证已解析的服务在预期的生命周期内有效,这个实现设计的非常高效,所以服务的解析速度非常快。

构建一个 IServiceProvider

首先,当我们把服务都添加到 IServiceCollection ,接下来会构建一个IServiceProvider, 它能够提供我们程序中所依赖服务的实例,本质上它包装了 IServiceCollection。

通过调用 BuildServiceProvider(IServiceCollection上的一个扩展方法)完成构建:

var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<ClassA>();
serviceCollection.AddSingleton<IThing, ClassB>();var serviceProvider = serviceCollection.BuildServiceProvider();

当我们没有传入任何参数时,它会创建一个 ServiceProviderOptions 的一个默认实例:

public static class ServiceCollectionContainerBuilderExtensions
{ public static ServiceProvider BuildServiceProvider(this IServiceCollection services){return services.BuildServiceProvider(ServiceProviderOptions.Default);}

ServiceProviderOptions 有两个属性,在本文后边的内容,我会详细介绍这些:

public class ServiceProviderOptions
{public bool ValidateScopes { get; set; }public bool ValidateOnBuild { get; set; }
}

BuildServiceProvider 的方法内部是这样的:

public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options)
{if (services == null){throw new ArgumentNullException(nameof(services));}if (options == null){throw new ArgumentNullException(nameof(options));}IServiceProviderEngine engine;
#if !NETCOREAPPengine = new DynamicServiceProviderEngine(services);
#elseif (RuntimeFeature.IsDynamicCodeCompiled){engine = new DynamicServiceProviderEngine(services);}else{// Don't try to compile Expressions/IL if they are going to get interpretedengine = new RuntimeServiceProviderEngine(services);}
#endifreturn new ServiceProvider(services, engine, options);
}

最终,它会创建并返回一个 ServiceProvider。

ServiceProviderEngine

在上面的代码中,ServiceProvider选择应该使用哪个 engine, engine 是一个组件,它的功能是负责 DI容器中服务实例的创建,然后把实例注入到其他服务中。

这些是 IServiceProviderEngine 的四个实现:

•Dynamic•Runtime•ILEmit•Expressions (System.Linq.Expressions)

从上面的代码中,我们可以看到在大多数情况下会使用 DynamicServiceProviderEngine,仅在目标框架不支持动态代码编译的情况下,才使用RuntimeServiceProviderEngine,DynamicServiceProviderEngine 会使用 ILEmit 或者 Expressions 来解析服务。

我们看一下 ServiceProviderEngine 的构造函数的内容:

protected ServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors)
{_createServiceAccessor = CreateServiceAccessor;Root = new ServiceProviderEngineScope(this);RuntimeResolver = new CallSiteRuntimeResolver();CallSiteFactory = new CallSiteFactory(serviceDescriptors);CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite());CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite());RealizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>();
}

它创建一个 Root ServiceProviderEngineScope,然后传入this, scopes限制了服务的生命周期,最常见的就是,.Net Core 收到一个接口请求时,它创建的服务就是 Scope 类型。

这种情况下,我们注册的单例服务,它都是从 Root Scope 返回的。

然后创建一个 CallSiteRuntimeResolver,我会在接下来的文章介绍它。

最后,在上面的构造函数中,将创建一个新的ConcurrentDictionary来保存有关服务的信息,按需设计,只有开始使用这些服务时,它才会开始创建,如果有些服务注册了,但是没有使用的话,那么它永远不会创建。

ServiceProvider 构造方法

让我们回到 BuildServiceProvider 方法的最后一行,它会传入 IServiceCollection, Engine和ServiceProviderOptions:

internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngine engine, ServiceProviderOptions options)
{_engine = engine;if (options.ValidateScopes){_engine.InitializeCallback(this);_callSiteValidator = new CallSiteValidator();}if (options.ValidateOnBuild){List<Exception> exceptions = null;foreach (ServiceDescriptor serviceDescriptor in serviceDescriptors){try{_engine.ValidateService(serviceDescriptor);}catch (Exception e){exceptions = exceptions ?? new List<Exception>();exceptions.Add(e);}}if (exceptions != null){throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray());}}
}

在上面的代码中,我们可以看到在构造函数中使用了ServiceProviderOptions, 当ValidateScopes为true时,ServiceProvider会传入this调用 engine 的 InitializeCallback方法,它还创建一个新的CallSiteValidator。

如果 ValidateOnBuild 为true的话,它会检查DI容器中已注册的所有服务,遍历了ServiceDescriptor 集合,然后调用 ValidateService, 检查服务,并且这里捕获了异常,如果有错误,会抛出一个聚合的异常信息。

那么在程序中使用 ValidateOnBuild,可以保证在程序启动时就检查已注册的错误服务,而不是在首次解析服务时在运行时捕获异常,这个可以很好的帮助排除问题。

ValidateService 的方法内部如下:

public void ValidateService(ServiceDescriptor descriptor)
{if (descriptor.ServiceType.IsGenericType && !descriptor.ServiceType.IsConstructedGenericType){return;}try{ServiceCallSite callSite = CallSiteFactory.GetCallSite(descriptor, new CallSiteChain());if (callSite != null){_callback?.OnCreate(callSite);}}catch (Exception e){throw new InvalidOperationException($"Error while validating the service descriptor '{descriptor}': {e.Message}", e);}
}

总结

在本文中,我们重点介绍了如何从IServiceCollection来构建IServiceProvider,我们探索了一些实现细节,以了解如何应用ValidateScopes和ValidateOnBuild ServiceProviderOptions,我们在这篇文章中谈到了很多内部代码,但作为库的使用者,您不必担心这些细节。

最重要的一点是,在IServiceCollection上调用BuildServiceProvider之后,将创建默认的ServiceProvider。

var serviceProvider = serviceCollection.BuildServiceProvider();

也可以传入 ServiceProviderOptions

var serviceProviderWithOptions = serviceCollection.BuildServiceProvider(new ServiceProviderOptions
{ValidateOnBuild = true,ValidateScopes = true
});

原文链接: https://www.stevejgordon.co.uk/aspnet-core-dependency-injection-what-is-the-iserviceprovider-and-how-is-it-built[2]

最后

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

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

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

相关文章

如何快速高效地学习互联网新技术

我们生活在一个振奋人心的时代&#xff0c;区块链、人工智能、大数据&#xff0c;现在各种新技术层出不穷&#xff0c;那么如何快速高效地学习互联网新技术呢&#xff1f;我认为首先思想要主动求变&#xff0c;敢于跳出自己的舒适区&#xff0c;对任何技术都抱有开放的心态。贪…

.NET Core用数据库做配置中心加载Configuration

本文介绍了一个在.NET中用数据库做配置中心服务器的方式&#xff0c;介绍了读取配置的开源自定义ConfigurationProvider&#xff0c;并且讲解了主要实现原理。1、 为什么用数据库做配置中心在开发youzack.com这个学英语网站的时候&#xff0c;需要保存第三方接口AppKey、JWT等配…

Abp Vnext Pro 的 Vue 实现版本

Abp Vnext Pro 的 Vue 实现版本开箱即用的中后台前端/设计解决方案知识点.Net Core5.0Abp Vnext 4.x ,Ant Design, Vue2.xMysql,Redis,Hangfire,ES(日志可选),Nocas(可选,未集成,计划中),RabbitMq(未集成,计划中)微服务架构设计, DDD 实践容器化 CI CD系统功能用户管理角色管理…

2018年,该转行AI工程师吗?

如此火爆的AI&#xff0c;会不会像Android和iOS一样&#xff0c;五年后归于平淡&#xff1f;转型AI真的有必要吗&#xff1f;2017年&#xff0c;AI就像一个点石成金的神器&#xff0c;所有的行业&#xff0c;任何的创业&#xff0c;抑或是职位背景&#xff0c;只要沾着这个词&a…

使用 Tye 辅助开发 dotnet 应用程序

newbe.pro 已经给我们写了系列文章介绍Tye 辅助开发k8s 应用&#xff1a;使用 Tye 辅助开发 k8s 应用竟如此简单&#xff08;一&#xff09;使用 Tye 辅助开发 k8s 应用竟如此简单&#xff08;二&#xff09;使用 Tye 辅助开发 k8s 应用竟如此简单&#xff08;三&#xff09;使…

你感兴趣的大学专业真相 | 16万人参与调查,看完80%都哭了

俗话说&#xff0c;隔行如隔山学科专业也是如此你以为我的专业十分高大上事实却是我也不知道我在学啥下面就由各个专业的同学为你揭秘他们专业的真相~人文社会类法学▼泛泛SaMa&#xff1a;在我们法学界&#xff0c;唯有秃头这件事&#xff0c;不分男女不墮紅塵&#xff1a;第一…

通过 .NET NativeAOT 实现用户体验升级

前言TypedocConverter 是我先前因帮助维护 monaco-editor-uwp 但苦于 monaco editor 的 API 实在太多&#xff0c;手写 C# 的类型绑定十分不划算而发起的一个项目。这个工具可以将 typedoc 根据 TypeScript 生成的 JSON 文件直接生成对应的 C# 类型绑定代码&#xff0c;并提供完…

序列每天从0开始_序列比对(十一)——计算符号序列的全概率

前文介绍了在知道符号序列后用viterbi算法求解最可能路径。本文介绍了如何使用前向算法和后向算法计算符号序列的全概率。如果一个符号序列中每个符号所对应的状态是已知的&#xff0c;那么这个符号序列出现的概率是容易计算的&#xff1a;但是&#xff0c;如果一个符号序列中每…

SQL 2005 使用row_number来分页

今天研究了一下row_number,用它来返回特定行的记录感觉是非常方便的&#xff0c;所以就做了个分页的存储过程&#xff0c;但不知道性能较之top和游标之类的那个好 代码 createprocedure[dbo].[proc_TestPage]--表名 tablenamenvarchar(255), --排序字段 sortcolumnnvarchar(255…

债务大爆发,中国30%家庭不堪一击!

债务大爆发&#xff0c;30%中国家庭“不堪一击”&#xff01;从2007年到2016年&#xff0c;中国家庭的债务率翻了一倍多。已经有超过1/3的家庭属于高负债家庭。前不久&#xff0c;深圳中兴网信科技有限公司的一研发组主管欧某&#xff0c;以最决绝的方式&#xff0c;从中兴通迅…

腾讯35k招.NET Core开发,深扒这些技术要求 真的很难吗?

3月草长莺飞&#xff0c;3月招聘满天飞&#xff0c;各种高薪招聘更是心里种草&#xff0c;前几天分享了腾讯牛年35k的.NET Core招聘需求&#xff0c;分享了一波资料深受好评&#xff0c;本着再接再厉的精神&#xff0c;本文继续为大家上干货&#xff0c;补齐腾讯的各种要求。新…

【直观理解】为什么梯度的负方向是局部下降最快的方向?

推荐阅读时间&#xff1a;8min~15min主要内容&#xff1a;为什么梯度的负方向是局部下降最快的方向&#xff1f;刚接触梯度下降这个概念的时候&#xff0c;是在学习机器学习算法的时候&#xff0c;很多训练算法用的就是梯度下降&#xff0c;然后资料和老师们也说朝着梯度的反方…

紫光物联linux登录账号,紫光展锐打造操作系统生态,赋能万物互联智能时代

本周&#xff0c;以“象由芯生科技服务人民”为主题的2020紫光展锐市场峰会重磅开启&#xff0c;广大生态合作伙伴共聚一堂&#xff0c;共话数字世界新未来。在今天举办的“操作系统OS研讨会”上&#xff0c;来自紫光展锐工程一线的架构师带来了一场整个操作系统领域的饕餮盛宴…

一行命令搭建内部的管道

在上一篇《边缘计算k8s集群之SuperEdge》文章中&#xff0c;笔者基于ECK搭建了边缘集群并添加了节点。通过边缘集群&#xff0c;我们可以很方便的管理各个地域的节点&#xff0c;本地、各云厂商的机房、客户所在地、海外的都可以。在本篇内容&#xff0c;我们将讲述如何使用ips…

ArchiMate - 发布【企业架构语言ArchiMate v0.5.pdf】

在《年度总结和计划&#xff1a;去年4个1&#xff0c;今年5个1》中说过今年我准备在项目组引入1个架构语言&#xff08;ArchiMate&#xff09;&#xff0c;为了便于大家学习&#xff0c;我把一些内容集成一本电子书&#xff0c;目前发布0.5版本&#xff0c;后续还会不断更新&am…

那些有趣/用的 Python 库

图片处理pip install pillowfrom PIL import Imageimport numpy as npa np.array(Image.open(test.jpg))b [255,255,255] - aim Image.fromarray(b.astype(uint8))im.save(new.jpg)youtube-dl下载国外视频pip install youtube-dl #直接安装youtube-dlpip install -U youtube…

linux系统刷分辨率,Linux下设置其分辨率及刷新率

行频&#xff1a;行频又称为“水平扫描频率”&#xff0c;指电子枪每秒在荧光屏上扫过的水平线的数量&#xff0c;其值等于“场频 垂直分辨率1.04”&#xff0c;单位为KHz(千赫兹)。行频是一个综合分辨率和场频的参数&#xff0c;该值越大&#xff0c;显示器可以提供的分辨率越…

.NET 5 部署在docker上运行

1、创建站点创建一个ASP.NET Core Web应用程序&#xff0c;选中启用Docker支持。自动帮我们创建一个Dockerfile文件。2、编写Dockerfile文件dockerfile是一个文件格式的配置文件&#xff0c;用户可以使用dockerfile来快速构建自定义的镜像。由一行行命令语句组成&#xff0c;并…

Nexus:一站式私有仓库管理(NuGet、Maven、npm、Docker)

我们在日常开发中经常需要使用到私有仓库&#xff0c;比如 dotNET 中的 NuGet、Java 中的 Maven、前端的 npm&#xff0c;还有 Docker 镜像&#xff0c;每一个私有仓库各自管理&#xff0c;维护起来比较麻烦&#xff0c;而 Nexus 可以将其统一起来。本文将介绍 Nexus 的安装以及…

众里寻 Bug 千百度,蓦然回首,它却在隔壁老张处……

程序员与 Bug 是一对矛盾的存在&#xff0c;程序员既要在解决 Bug 中获得成就感&#xff0c;同时也讨厌 Bug 本身的存在。“程序不息&#xff0c;Bug 不止”&#xff0c;程序员在与 Bug 的斗争中&#xff0c;也有很多有趣的事情发生&#xff0c;我们整理了一些程序员在调试 Bug…