ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】

到目前为止,我们定义的ServiceProvider已经实现了基本的服务提供和回收功能,但是依然漏掉了一些必需的细节特性。这些特性包括如何针对IServiceProvider接口提供一个ServiceProvider对象,何创建ServiceScope,以及如何提供一个服务实例的集合。

一、提供一个ServiceProvider对象

我们知道当将服务类型指定为IServiceProvider接口并调用ServiceProvider的GetService方法是,ServiceProvider对象本身将会作为服务实例返回,这个特性可以利用一个自定义的Service来实现。如下面的代码片段所示,我们定义的这个ServiceProviderService既是一个Service,又是一个ServiceCallSite。它默认采用生命周期管理模式为Scoped,在Invoke和Build方法中,它直接将当前ServiceProvider作为提供的服务实例。在初始化ServiceTable的时候,我们额外添加一个针对ServiceProviderService的ServideEntry。

   1: internal class ServiceProviderService : IService, IServiceCallSite
   2: {
   3:     public ServiceLifetime Lifetime => ServiceLifetime.Scoped;
   4:     public IService Next { get; set; }
   5:  
   6:     public Expression Build(Expression provider)
   7:     {
   8:         return provider;
   9:     }
  10:  
  11:     public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
  12:     {
  13:         return this;
  14:     }
  15:  
  16:     public object Invoke(ServiceProvider provider)
  17:     {
  18:         return provider;
  19:     }
  20: }
  21:  
  22: internal class ServiceTable
  23: {
  24:     public ServiceTable(IServiceCollection services)
  25:     {
  26:         //解析ServiceCollection并添加相应ServiceEntry
  27:         this.ServieEntries[typeof(IServiceProvider)] = new ServiceEntry(new ServiceProviderService());
  28:     }
  29: }

二、创建ServiceScope

创建ServiceScope的目的在于创建作为当前ServiceProvider儿子的另一个ServiceProvider,新创建的ServiceProvider不仅与原来的ServiceProvider具有相同的根,同时共享所有的服务注册信息。利用这个新的ServiceProvider来代替现有的ServiceProvider,其主要的目的还是使我们能够及时地回收提供的服务实例。ServiceScope是通过它的工厂ServiceScopeFactory来创建的,所以先创建了如下一个ServiceScopeFactory类和对应的ServiceScope,它们的定义与我们在前面一节介绍的完全一致。

   1: internal class ServiceScope : IServiceScope
   2: {
   3:     public IServiceProvider ServiceProvider { get; private set; }
   4:  
   5:     public ServiceScope(ServiceProvider serviceProvider)
   6:     {
   7:         this.ServiceProvider = serviceProvider;
   8:     }
   9:  
  10:     public void Dispose()
  11:     {
  12:         (this.ServiceProvider as IDisposable)?.Dispose();
  13:     }
  14: }
  15:  
  16: internal class ServiceScopeFactory : IServiceScopeFactory
  17: {
  18:     public ServiceProvider ServiceProvider { get; private set; }
  19:  
  20:     public ServiceScopeFactory(ServiceProvider serviceProvider)
  21:     {
  22:         this.ServiceProvider = serviceProvider;
  23:     }
  24:  
  25:     public IServiceScope CreateScope()
  26:     {
  27:         return new ServiceScope(this.ServiceProvider);
  28:     }
  29: }
  30:  
  31: internal class ServiceProvider : IServiceProvider, IDisposable
  32: {
  33:     
  34:     public ServiceProvider(ServiceProvider parent)
  35:     {
  36:         this.Root = parent.Root;
  37:         this.ServiceTable = parent.ServiceTable;
  38:     }
  39: }

为了让ServiceProvider的GetService方法在服务类型指定为IServiceScopeFactory接口的时候能够自动返回上面我们定义的ServiceScopeFactory对象,我们依然和上面一样创建了一个自定义的Service,并将其命名为ServiceScopeFactoryService。与ServiceProviderService一样,ServiceScopeFactoryService同时也是一个ServiceCallSite,在Build和Invoke方法中它会返回一个ServiceScopeFactory对象。为了让这个它能够生效,我们依然在ServiceTable初始化的时自动添加一个相应的ServiceEntry。

   1: internal class ServiceScopeFactoryService : IService, IServiceCallSite
   2: {
   3:     public ServiceLifetime Lifetime=> ServiceLifetime.Scoped;
   4:     public IService Next { get; set; }
   5:  
   6:     public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)
   7:     {
   8:         return this;
   9:     }
  10:  
  11:     public Expression Build(Expression provider)
  12:     {
  13:         return Expression.New(typeof(ServiceScopeFactory).GetConstructors().Single(), provider);
  14:     }
  15:  
  16:     public object Invoke(ServiceProvider provider)
  17:     {
  18:         return new ServiceScopeFactory(provider);
  19:     }
  20: }
  21:  
  22: internal class ServiceTable
  23: {
  24:     public ServiceTable(IServiceCollection services)
  25:     {
  26:         //解析ServiceCollection并添加相应ServiceEntry
  27:         this.ServieEntries[typeof(IServiceProvider)] =  new ServiceEntry(new ServiceProviderService());
  28:         this.ServieEntries[typeof(IServiceScopeFactory)] = new ServiceEntry(new ServiceScopeFactoryService());
  29:     }
  30: }

三、提供一组服务的集合

到目前为止,我们自定义的ServiceProvider尚不具备原生ServiceProvider的一项特性,那就是当调用GetService方法时将服务类型指定为IEnumerable<T>或者直接调用扩展方法GetServices时,得到的是一个服务实例的集合。这个特性可以通过一个自定义的ServiceCallSite来完成,我们将其命名为EnumerableCallSite。

   1: internal class EnumerableCallSite : IServiceCallSite
   2: {
   3:     public Type ElementType { get; private set; }
   4:     public IServiceCallSite[] ServiceCallSites { get; private set; }
   5:  
   6:     public EnumerableCallSite(Type elementType, IServiceCallSite[] serviceCallSites)
   7:     {
   8:         this.ElementType = elementType;
   9:         this.ServiceCallSites = serviceCallSites;
  10:     }
  11:  
  12:     public Expression Build(Expression provider)
  13:     {
  14:         return Expression.NewArrayInit(this.ElementType, this.ServiceCallSites.Select(
  15:             it => Expression.Convert(it.Build(provider), this.ElementType)));
  16:     }
  17:  
  18:     public object Invoke(ServiceProvider provider)
  19:     {
  20:         var array = Array.CreateInstance(this.ElementType, this.ServiceCallSites.Length);
  21:         for (var index = 0; index < this.ServiceCallSites.Length; index++)
  22:         {
  23:             array.SetValue(this.ServiceCallSites[index].Invoke(provider), index);
  24:         }
  25:         return array;
  26:     }
  27: }

如上面的代码片段所示,EnumerableCallSite具有两个两个只读属性(ElementType和ServiceCallSites),前者表示返回的服务集合的元素类型,后者则返回一组用于提供集合元素的ServiceCallSite。在Invoke和Build方法中,我们只需要根据元素类型创建一个数组,并利用这组ServiceCallSite创建所有的元素即可。这个EnumerableCallSite最终按照如下的方式应用到ServiceProvider的GetServiceCallSite方法中。

   1: internal class ServiceProvider : IServiceProvider, IDisposable
   2: { 
   3:     public IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain)
   4:     {
   5:         try
   6:         {
   7:             if (callSiteChain.Contains(serviceType))
   8:             {
   9:                 throw new InvalidOperationException(string.Format("A circular dependency was detected for the service of type '{0}'",serviceType.FullName);
  10:             }
  11:             callSiteChain.Add(serviceType);
  12:             ServiceEntry serviceEntry;
  13:             if (this.ServiceTable.ServieEntries.TryGetValue(serviceType, out serviceEntry))
  14:             {
  15:                 return serviceEntry.Last.CreateCallSite(this, callSiteChain);
  16:             }
  17:  
  18:             if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition()== typeof(IEnumerable<>))
  19:             {
  20:                 Type elementType = serviceType.GetGenericArguments()[0];
  21:                 IServiceCallSite[] serviceCallSites = this.ServiceTable.ServieEntries.TryGetValue(elementType, out serviceEntry)
  22:                     ? serviceEntry.All.Select(it => it.CreateCallSite(this, callSiteChain)).ToArray()
  23:                     : new IServiceCallSite[0];
  24:                 return new EnumerableCallSite(elementType, serviceCallSites);
  25:             }
  26:  
  27:             return null;
  28:         }
  29:         finally
  30:         {
  31:             callSiteChain.Remove(serviceType);
  32:         }
  33:     }
  34:     //其他成员
  35: }

 

ASP.NET Core中的依赖注入(1):控制反转(IoC)
ASP.NET Core中的依赖注入(2):依赖注入(DI)
ASP.NET Core中的依赖注入(3):服务注册与提取
ASP.NET Core中的依赖注入(4):构造函数的选择与生命周期管理
ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【总体设计】
ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【解读ServiceCallSite】
ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文链接

时间: 2024-10-28 10:39:55

ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】的相关文章

ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理

ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationInstance和ImplementationFactory属性均为Null,那么ServiceProvider最终会利用其ImplementationType属性返回的真实类型选择一个适合的构造函数来创建最终的服务实例.我们知道服务服务的真实类型可以定义了多个构造函数,那么ServiceProvid

ASP.NET Core中的依赖注入(1):控制反转(IoC)

ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化",我们将这些标准化的组件称为服务,ASP.NET在内部专门维护了一个DI容器来提供所需的服务.要了解这个DI容器以及现实其中的服务提供机制,我们先得知道什么是DI(Dependence Injection),而一旦我们提到DI,又不得不说IoC(Inverse of Control). 目录 一.流程控

ASP.NET Core中的依赖注入(2):依赖注入(DI)

IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计模式以不同的方式实现IoC,比如我们在上面介绍的模板方法.工厂方法和抽象工厂,接下来我们介绍一种更为有价值的IoC模式,即依赖注入(DI:Dependency Injection,以下简称DI). 目录 一.由外部容器提供服务对象 二.三种依赖注入方式     构造器注入     属性注入     方法注入

ASP.NET Core中的依赖注入(3): 服务的注册与提供

在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core中的DI容器体现为一个实现了IServiceProvider接口的对象. ServiceProvider与ServiceDescriptor 服务的注册与提供     利用ServiceProvider来提供服务     提供一个服务实例的集合     获取ServiceProvider自身对象  

ASP.NET Core 中的依赖注入 [共7篇]

一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化",我们将这些标准化的组件称为服务,ASP.NET在内部专门维护了一个DI容器来提供所需的服务.要了解这个DI容器以及现实其中的服务提供机制,我们先得知道什么是DI(Dependence Injection),而一旦我们提到DI,又不得不说IoC(Inverse of Contro

ASP.NET Core中如影随形的”依赖注入”[下]: 历数依赖注入的N种玩法

在对ASP.NET Core管道中关于依赖注入的两个核心对象(ServiceCollection和ServiceProvider)有了足够的认识之后,我们将关注的目光转移到编程层面.在ASP.NET Core应用中基于依赖注入的编程主要涉及到两个方面,它们分别是将服务注册到ServiceCollection中,和采用注入的方式利用ServiceProvider提供我们所需的服务.我们先来讨论ASP.NET Core应用中如何进行服务注册.[本文已经同步到<ASP.NET Core框架揭秘>之中

浅析Node.js中使用依赖注入的相关问题及解决方法

这篇文章主要介绍了浅析Node.js中使用依赖注入的相关问题及解决方法,Node.js是一个将JavaScript应用运行于服务器端的框架,需要的朋友可以参考下 最近,我转向使用依赖注入来帮助理解分离代码的简单途径,并有助测试.然而,Node.js中的模块依赖Node提供的系统API,这很难判断私有依赖被恰当的使用.一般的依赖注入很难在这种情况下使用,但现在不要放弃希望. requireCauses 问题 Node.js很容易依照需求导入依赖.它运行的很好,并且比AMD模式加载器例如Requir

详解AngularJS中的依赖注入机制

  这篇文章主要介绍了详解AngularJS中的依赖注入机制,对JavaScript各组件的使用起到非常重要的作用,需要的朋友可以参考下 依赖注入是一个在组件中给出的替代了硬的组件内的编码它们的依赖关系的软件设计模式.这减轻一个组成部分,从定位的依赖,依赖配置.这有助于使组件可重用,维护和测试. AngularJS提供了一个至高无上的依赖注入机制.它提供了一个可注入彼此依赖下列核心组件. 值 工厂 服务 提供者 常值 值 值是简单的JavaScript对象,它是用来将值传递过程中的配置相位控制器

JavaScript中的依赖注入详解

 这篇文章主要介绍了JavaScript中的依赖注入详解,本文讲解了requirejs/AMD方法.反射(reflection)方法等内容,需要的朋友可以参考下     计算机编程的世界其实就是一个将简单的部分不断抽象,并将这些抽象组织起来的过程.JavaScript也不例外,在我们使用JavaScript编写应用时,我们是不是都会使用到别人编写的代码,例如一些著名的开源库或者框架.随着我们项目的增长,我们需要依赖的模块变得越来越多,这个时候,如何有效的组织这些模块就成了一个非常重要的问题.依赖