探索 .NET Core 依赖注入的 IServiceProvider
在上一篇文章中,我们学习了Microsoft.Extensions.DependencyInjection
中的IServiceCollection,包括服务注册转换为ServiceDescriptors,然后添加到集合中。
"探索 .NET Core 依赖注入的 IServiceCollection"
在本文中,我们会学习 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 !NETCOREAPP
engine = new DynamicServiceProviderEngine(services);
#else
if (RuntimeFeature.IsDynamicCodeCompiled)
{
engine = new DynamicServiceProviderEngine(services);
}
else
{
// Don't try to compile Expressions/IL if they are going to get interpreted
engine = new RuntimeServiceProviderEngine(services);
}
#endif
return 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
});
最后
欢迎扫码关注我们的公众号 【全球技术精选】,专注国外优秀博客的翻译和开源项目分享,也可以添加QQ群 897216102
探索 .NET Core 依赖注入的 IServiceProvider的更多相关文章
- 探索 .NET Core 依赖注入的 IServiceCollection
如果您使用了.NET Core,则很可能已使用Microsoft.Extensions.DependencyInjection中的内置依赖项注入容器,在本文中,我想更深入地了解Microsoft De ...
- Webservice WCF WebApi 前端数据可视化 前端数据可视化 C# asp.net PhoneGap html5 C# Where 网站分布式开发简介 EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下? SQL Server之深入理解STUFF 你必须知道的EntityFramework 6.x和EntityFramework Cor
Webservice WCF WebApi 注明:改编加组合 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下, ...
- # ASP.NET Core依赖注入解读&使用Autofac替代实现
标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...
- NET Core依赖注入解读&使用Autofac替代实现
NET Core依赖注入解读&使用Autofac替代实现 标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. ...
- 实现BUG自动检测 - ASP.NET Core依赖注入
我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评. 如果以后有时间的话,我打算写一个系列的[实现BUG自动检测 ...
- [译]ASP.NET Core依赖注入深入讨论
原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...
- asp.net core 依赖注入几种常见情况
先读一篇注入入门 全面理解 ASP.NET Core 依赖注入, 学习一下基本使用 然后学习一招, 不使用接口规范, 直接写功能类, 一般情况下可以用来做单例. 参考https://www.cnblo ...
- ASP.NET Core依赖注入——依赖注入最佳实践
在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...
- 自动化CodeReview - ASP.NET Core依赖注入
自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...
随机推荐
- Centos根目录100%解决思路
Centos 7 根目录(爆满)100%解决思路,下面以宝塔面板环境为例 1.首先远程到服务器,在~下输入df -h ,看下根目录下文件夹使用情况 [root@localhost~]# df -h F ...
- Luogu T16048 会议选址
本题idea版权来自CSDN博客Steve_Junior的医院设置2. 并没有什么用的链接 题目背景 \(A\)国的国情十分独特.它总共有\(n\)个城市,由\(n-1\)条道路连接.国内的城市当然是 ...
- 2019牛客多校 Round7
Solved:5 Rank:296 E Find the median (线段树) 题意:最开始一个空的数组 4e5次操作 每次把Li,Ri中的每个数插入进来 问当前的中位数 题解:把这n个区间离散化 ...
- Codeforces Round #647 (Div. 2) C. Johnny and Another Rating Drop(数学)
题目链接:https://codeforces.com/contest/1362/problem/C 题意 计算从 $0$ 到 $n$ 相邻的数二进制下共有多少位不同,考虑二进制下的前导 $0$ .( ...
- Educational Codeforces Round 102 (Rated for Div. 2) B. String LCM (构造,思维)
题意:给你两个字符串\(a\)和\(b\),找出它们的\(lcm\),即构造一个新的字符串\(c\),使得\(c\)可以由\(x\)个\(a\)得到,并且可以由\(y\)个\(b\)得到,输出\(c\ ...
- fzu2202 犯罪嫌疑人
Problem Description 福尔摩斯是个大侦探,他总是在解决疑难案件.这一次的案件也不例外,案件是这样的:有编号为1到N的N位嫌疑犯,他们其中有一个犯了罪,然后每个嫌疑犯都被询问,&quo ...
- WPF 只读集合在 XAML 中的绑定(WPF:Binding for readonly collection in xaml)
问题背景 某一天,我想做一个签到打卡的日历.基于 Calendar,想实现这个目标,于是找到了它的 SelectedDates 属性,用于标记签到过的日期. 问题来了. 基于MVVM模式,想将其在xa ...
- 详解Go语言I/O多路复用netpoller模型
转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 本文使用的go的源码15.7 可以从 Go 源码目录结构和对应代码文件了解 Go 在不同 ...
- Base64 编码原理
什么是 Base64 编码 Base64 编码是最常见的编码方式,基于 64 个可打印字符来表示任意二进制数据的方法,是从二进制转换到可见字符的过程. 使用场景 数据加密或签名通过 Base64 转换 ...
- 实战交付一套dubbo微服务到k8s集群(1)之Zookeeper部署
基础架构 主机名 角色 IP地址 mfyxw10.mfyxw.com K8S代理节点1,zk1 192.168.80.10 mfyxw20.mfyxw.com K8S代理节点2,zk2 192.168 ...