前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >探索 .NET Core 依赖注入的 IServiceProvider

探索 .NET Core 依赖注入的 IServiceProvider

作者头像
全球技术精选
发布2021-03-03 11:39:03
1.2K0
发布2021-03-03 11:39:03
举报
文章被收录于专栏:全球技术精选全球技术精选

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

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

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

什么是 IServiceProvider?

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

构建一个 IServiceProvider

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

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

代码语言:javascript
复制
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<ClassA>();
serviceCollection.AddSingleton<IThing, ClassB>();

var serviceProvider = serviceCollection.BuildServiceProvider();

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

代码语言:javascript
复制
public static class ServiceCollectionContainerBuilderExtensions
{ 
    public static ServiceProvider BuildServiceProvider(this IServiceCollection services)
    {
        return services.BuildServiceProvider(ServiceProviderOptions.Default);
    }

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

代码语言:javascript
复制
public class ServiceProviderOptions
{
    public bool ValidateScopes { get; set; }
    public bool ValidateOnBuild { get; set; }
}

BuildServiceProvider 的方法内部是这样的:

代码语言:javascript
复制
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 的构造函数的内容:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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 的方法内部如下:

代码语言:javascript
复制
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。

代码语言:javascript
复制
var serviceProvider = serviceCollection.BuildServiceProvider();

也可以传入 ServiceProviderOptions

代码语言:javascript
复制
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]

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 半栈程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是 IServiceProvider?
  • 构建一个 IServiceProvider
  • ServiceProviderEngine
  • ServiceProvider 构造方法
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档