到目前为止,我们定义的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. .net core 产品开发问题记录

    背景 最近在公司的一个产品研发中,最终还是选择了以.net core 作为主要的技术方案.本文会拟记录开发过程中于以往中区别比较大,或者可能造成的坑. 程序集无法引用本地程序集 .net core 的 ...

  2. 【WPF】WPF中的List<T>和ObservableCollection<T>

    在WPF中 控件绑定数据源时,数据源建议采用 ObservableCollection<T>集合 ObservableCollection<T> 类:表示一个动态数据集合,在添 ...

  3. php安装配置那些事(本文纯属个人记事与技术无关)

    上周由于项目需要,又拿起了三年没动过的php,从安装环境到配置,大体已经忘干净,于是咨询同学问度娘,终于在我的win7系统下安装了xampp的集成环境+NetBeans IDE 8.0,于是导入项目文 ...

  4. python中的参数问题

    python中的有默认参数和可变参数之分 默认参数arg 可变参数args, kargs 默认参数arg就是调用指定参数 可变参数*arg调用时传入的的参数会被python自动包装为列表 可变参数ka ...

  5. 代码-->发呆

    代码,敲着敲着,就发呆了. 其实安安静静敲代码,是一种享受.开着不快不慢的音乐,徜徉在代码的世界里,忘记了时间的存在. 原本备忘录中提醒我13点看书,一小时又一小时的延后,已经不记得自己按了多少次一小 ...

  6. 关于learntorank http://qiita.com/rockhopper/items/bb3d46f01df5f6499123

    一.数据转换 如何对于训练数据做pairwise的transform,比如你原始数据是要么点击要么不点击,如何对这些样本数据做pairwise的transform? 下面的方法主要是做组合的方法,就是 ...

  7. 如何在select下拉列表中添加复选框?

    近来在给一个公司做考试系统的项目,遇到的问题不少,但其中的几个让我对表单的使用颇为感兴趣,前端程序员都知道,下拉列表有select标签,复选框有checkbox,但是两者合在一起却少有人去研究,当时接 ...

  8. JS的面向对象编程一:封装

    Javascript是一种基于对象的语言,但它又没有class.这在很长的一段时间里,对JS的面向对象编程的概念很模糊,在编程的时候时有用到,但要说个所以然,却说不出来,所以看了些书,又在网上查了些资 ...

  9. WebService的一些案例

    既然要实现WebService,首先先来创建一个Service类 package cn.happy.webservice; import javax.jws.WebService; import ja ...

  10. 在Excel中把横行与竖列进行置换、打勾号

    在Excel中把横行与竖列进行置换:复制要置换的单元,在新的单元上右键->选择性复制,会出现对话框,选中“置换”,即可在Excel中打勾号,左手按住ALT不放,右手在小键盘也就是右边的数字键盘依 ...