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实现揭秘【补充漏掉的细节】

ServicePrvider实现揭秘的更多相关文章

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

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

  2. ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】

    本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...

  3. ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【解读ServiceCallSite 】

    通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的.ServiceProvider最 ...

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

    ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...

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

    IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...

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

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

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

    ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...

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

    一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了 ...

  9. NET Core 中的依赖注入

    NET Core 中的依赖注入 [共7篇] 一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制, ...

随机推荐

  1. 相遇Qt5

    使用Qt5.x版本中的不同方面来开发应用程序,着重于新的Qt Quick的技术,提供了编写C++后端的必要内容,并扩展了Qt Quick.     本章提供了关于Qt5高层次的概述.它对开发者有效的展 ...

  2. BMP文件结构

    1. 位图文件头 位图文件头包含有关于文件类型.文件大小.存放位置等信息,在Windows 3.0以上版本的位图文件中用BITMAPFILEHEADER结构来定义: typedef struct ta ...

  3. Java中怎么控制线程訪问资源的数量

    在API中是这样来描写叙述Semaphore 的 Semaphore 通经常使用于限制能够訪问某些资源(物理或逻辑的)的线程数目. 一个计数信号量.从概念上讲,信号量维护了一个许可集.如有必要,在许可 ...

  4. SDUTOJ 1489 求二叉树的先序遍历

    <img src="http://img.blog.csdn.net/20141014212549703?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi ...

  5. Android调用系统相机、自己定义相机、处理大图片

    Android调用系统相机和自己定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,而且因为涉及到要把拍到的照片显示出来,该样例也会涉及到Android载入大图片时候的处 ...

  6. Codeforces 39E What Has Dirichlet Got to Do with That? 游戏+内存搜索

    主题链接:点击打开链接 意甲冠军: 特定 a一箱 b球 不变n (球和箱子都不尽相同,样的物品) 设 way = 把b个球放到a个箱子中的方法数, 若way >= n则游戏结束 有2个人玩游戏. ...

  7. Android动态布局,并动态为TextView控件设置drawableLeft、drawableRight等属性加入图标

    注:(图中每个条目和图标都是由代码动态生成) 代码动态布局,并须要为每个条目设置图标,此时用到了 android:drawableLeft="@drawable/icon"  父x ...

  8. wiki 3143 二叉树的前序、中序及后序遍历

    先序遍历:訪问根.遍历左子树.遍历右子树,简称:DLR. 中序遍历:遍历左子树,訪问根,遍历右子树,简称:LDR. 后序遍历:遍历左子树,遍历右子树.訪问根.简称:LRD. 数组搞的: #pragma ...

  9. python爬虫抓站的一些技巧总结

    使用python爬虫抓站的一些技巧总结:进阶篇 一.gzip/deflate支持现在的网页普遍支持gzip压缩,这往往可以解决大量传输时间,以VeryCD的主页为例,未压缩版本247K,压缩了以后45 ...

  10. JSCapture实现屏幕捕捉

    JSCapture 是用纯 JavaScript 和 HTML5 实现的屏幕捕捉库. 能够随意在浏览器或者桌面视频进行截图, JSCapture 使用 getUserMedia 来实现屏幕捕获. 当前 ...