到目前为止,我们定义的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实现揭秘【补充漏掉的细节】

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

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

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

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

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

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

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

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

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

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

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

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

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

  7. ASP.NET Core 中的依赖注入

    目录 什么是依赖注入 ASP .NET Core 中使用依赖注入 注册 使用 释放 替换为其它的 Ioc 容器 参考 什么是依赖注入 软件设计原则中有一个依赖倒置原则(DIP),为了更好的解耦,讲究要 ...

  8. ASP.NET Core 中的 依赖注入介绍

    ASP.NET Core 依赖注入 HomeController public class HomeController : Controller { private IStudentReposito ...

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

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

随机推荐

  1. 关于node.js

    JS是一种脚本语言,它的本身并不能进行编译和执行,在最早的时期只是作为浏览器的脚本,只能够在浏览器中执行操作,也就是说JS必须依赖一个运行环境作为载体才能够执行. 而nodejs是基于chromeV8 ...

  2. errno

    关于errno有以下需要注意: 1  A common mistake is to do if (somecall() == -1) {                printf("som ...

  3. [ios]关于用FMDB 操作数据库 删除 tableView 后刷新

    刚了解使用fmdb,从数据库获取数据 绑定到一个可变数组classNameItems //从ClassList表取得数据 FMResultSet *classInfo=[db executeQuery ...

  4. 如何将本地的jar包上传到maven本地仓库中

    首先需要将本地的jar包做mvn install到本地仓库中 mvn install:install-file -Dfile=D:\skyeye-ruleInfo\lib\lucene-querypa ...

  5. 1002. A+B for Polynomials (25)

    题目链接:https://www.patest.cn/contests/pat-a-practise/1002 原题如下: This time, you are supposed to find A+ ...

  6. 安装OS X虚拟机错误vcpu-0:VERIFY vmcore/vmm/main/physMem_monitor.c:1123

    新建一个虚拟机, 选择客户机操作系统为Apple MacOS X 10.10, 其余参数可以默认. 注意建好之后不要急着打开客户机, 因为直接打开你会发现新建的客户机将会无法启动. 仔细阅读Mac O ...

  7. SQL Server 触发器

    触发器是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程.触发器主要是通过事件进行触发被自动调用执行的.而存储过程可以通过存储过程的名称被调用. Ø 什么是触发器 触发器对表进行插入.更新.删 ...

  8. linux进程管理(上)

    程序和进程的区别: 1.程序是一种静态资源 程序启动产生进程 2.程序与进程无一一对应原则  进程是动态的一个过程 父进程和子进程在前面提过 前台进程:执行命令时只能等待的进程为前台进程也叫异步进程 ...

  9. Filter 数组过滤函数精解示例

    '************************************************************************* '**模 块 名:Filter 数组过滤函数精解示 ...

  10. 反向输出及sort排序

    建立条件:#include "algorithm"引用这个头文件 1.reverse 的用法,反向排序,由自己输入5个数: 1 2 3 4 5 for (int i = 0; i ...