通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的。ServiceProvider最终采用何种方式提供我们所需的服务实例取决于最终选择了怎样的ServiceCallSite,而服务注册是采用的ServiceDescriptor有决定了ServiceCallSite类型的选择。我们将众多不同类型的ServiceCallSite大体分成两组,一组用来创建最终的服务实例,另一类则与生命周期的管理有关。

一、用于服务创建的ServiceCallSite

服务实例的创建方式主要有三种,分别对应ServiceDescriptor如下三个只读属性。简单来说,如果ImplementationInstance属性返回一个具体的对象,该对象将直接作为提供的服务实例。如果属性ImplementationFactory返回一个具体的委托对象,该委托将会作为提供服务实例的工厂。除此之外,ServiceProvider将会利用ImplementationType属性返回的真是服务类型定位某一个最佳的构造函数来创建最终提供的服务实例。

   1: public class ServiceDescriptor

   2: {

   3:     public Type                               ImplementationType {  get; }

   4:     public object                             ImplementationInstance {  get; }

   5:     public Func<IServiceProvider, object>     ImplementationFactory {  get; }      

   6: }

服务实例的这三种不同的创建方式最终由三种对应的ServiceCallSite类型来完成,我们将它们的类型分别命名为InstanceCallSite、FactoryCallSite和ConstructorCallSite。如下面的代码片段所示,前两种ServiceCallSite(InstanceCallSite和FactoryCallSite)的实现非常简单,所以我们在这里就不对它们多做介绍了。

   1: internal class InstanceCallSite : IServiceCallSite

   2: {

   3: public object Instance { get; private set; }

   4:  

   5:     public InstanceCallSite(object instance)

   6:     {

   7:         this.Instance = instance;

   8:     }

   9:     public Expression Build(Expression provider)

  10:     {

  11:         return Expression.Constant(this.Instance);

  12:     }

  13:     public object Invoke(ServiceProvider provider)

  14:     {

  15:         return Instance;

  16:     }

  17: }

  18:  

  19: internal class FactoryCallSite : IServiceCallSite

  20: {

  21:     public Func<IServiceProvider, object> Factory { get; private set; }

  22:     public FactoryCallSite(Func<IServiceProvider, object> factory)

  23:     {

  24:         this.Factory = factory;

  25:     }

  26:     public Expression Build(Expression provider)

  27:     {

  28:         Expression<Func<IServiceProvider, object>> factory = p => this.Factory(p);

  29:         return Expression.Invoke(factory, provider);

  30:     }

  31:     public object Invoke(ServiceProvider provider)

  32:     {

  33:         return this.Factory(provider);

  34:     }

  35: }

以执行指定构造函数创建服务实例的ConstructorCallSite稍微复杂一点。如下面的代码片段所示,我们在创建一个ConstructorCallSite对象的时候除了指定一个代表构造函数的ConstructorInfo对象之外,还需要指定一组用于初始化对应参数列表的ServiceCallSite。

   1: internal class ConstructorCallSite : IServiceCallSite

   2: {

   3:     public ConstructorInfo ConstructorInfo { get; private set; }

   4:     public IServiceCallSite[] Parameters { get; private set; }

   5:  

   6:     public ConstructorCallSite(ConstructorInfo constructorInfo, IServiceCallSite[] parameters)

   7:     {

   8:         this.ConstructorInfo = constructorInfo;

   9:         this.Parameters = parameters;

  10:     }

  11:  

  12:     public Expression Build(Expression provider)

  13:     {

  14:         ParameterInfo[] parameters = this.ConstructorInfo.GetParameters();

  15:         return Expression.New(this.ConstructorInfo, this.Parameters.Select((p, index) => Expression.Convert(p.Build(provider), 

  16:             parameters[index].ParameterType)).ToArray());

  17:     }

  18:  

  19:     public object Invoke(ServiceProvider provider)

  20:     {

  21:         return this.ConstructorInfo.Invoke(this.Parameters.Select(p => p.Invoke(provider)).ToArray());

  22:     }

  23: }

虽然ConstructorCallSite自身创建服务实例的逻辑很简单,但是如何创建ConstructorCallSite对象本身相对麻烦一些,因为这涉及到如何选择一个最终构造函数的问题。我们在上面专门介绍过这个问题,并且总结出选择构造函数采用的两条基本的策略:

  • ServiceProvider能够提供构造函数的所有参数。
  • 目标构造函数的参数类型集合是所有有效构造函数参数类型集合的超级。

我们将ConstructorCallSite的创建定义在Service类的CreateConstructorCallSite方法中,它具有额外两个辅助方法GetConstructor和GetParameterCallSites,前者用于选择正确的构造函数,后者则为指定的构造函数创建用于初始化参数的ServiceCallSite列表。

   1: internal class Service : IService

   2: {

   3:     private ConstructorCallSite CreateConstructorCallSite(ServiceProvider provider, ISet<Type> callSiteChain)

   4:     {

   5:         ConstructorInfo constructor = this.GetConstructor(provider, callSiteChain);

   6:         if (null == constructor)

   7:         {

   8:             throw new InvalidOperationException("No avaliable constructor");

   9:         }

  10:         return new ConstructorCallSite(constructor, constructor.GetParameters().Select(p => provider.GetServiceCallSite(p.ParameterType, callSiteChain)).ToArray());                                              

  11: }

  12:  

  13:     private ConstructorInfo GetConstructor(ServiceProvider provider, ISet<Type> callSiteChain)

  14:     {

  15:         ConstructorInfo[] constructors = this.ServiceDescriptor.ImplementationType.GetConstructors()

  16:             .Where(c => (null != this.GetParameterCallSites(c, provider, callSiteChain))).ToArray();

  17:  

  18:         Type[] allParameterTypes = constructors.SelectMany(

  19:             c => c.GetParameters().Select(p => p.ParameterType)).Distinct().ToArray();

  20:  

  21:         return constructors.FirstOrDefault(

  22:             c => new HashSet<Type>(c.GetParameters().Select(p => p.ParameterType)).IsSupersetOf(allParameterTypes));

  23:     }

  24:  

  25:     private IServiceCallSite[] GetParameterCallSites(ConstructorInfo constructor,ServiceProvider provider,ISet<Type> callSiteChain)

  26:     {

  27:         ParameterInfo[] parameters = constructor.GetParameters();

  28:         IServiceCallSite[] serviceCallSites = new IServiceCallSite[parameters.Length];

  29:  

  30:         for (int index = 0; index < serviceCallSites.Length; index++)

  31:         {

  32:             ParameterInfo parameter = parameters[index];

  33:             IServiceCallSite serviceCallSite = provider.GetServiceCallSite(

  34:                 parameter.ParameterType, callSiteChain);

  35:             if (null == serviceCallSite && parameter.HasDefaultValue)

  36:             {

  37:                 serviceCallSite = new InstanceCallSite(parameter.DefaultValue);

  38:             }

  39:             if (null == serviceCallSite)

  40:             {

  41:                 return null;

  42:             }

  43:             serviceCallSites[index] = serviceCallSite;

  44:         }

  45:         return serviceCallSites;

  46:     }

  47:     //其他成员

  48: }

二、用于管理生命周期的ServiceCallSite

服务实例最终采用何种提供方式还与服务注册时采用的生命周期管理模式有关,三种不同的生命周期管理模式(Transient、Scoped和Singleton)分别对应着三种不同的ServiceCallSite类型,我们分别将其命名为TransienCallSite、ScopedCallSite和SingletonCallSite。

对于TransientCallSite来说,它提供服务实例的方式非常直接,那就是直接创建一个新的对象。在此之外还需要考虑一个关于服务回收的问题,那就是如果服务实例对应的类型实现了IDisposable接口,需要将它添加到ServiceProvider的TransientDisposableServices属性中。TransientCallSite具有如下的定义,只读属性ServiceCallSite表示真正用于创建服务实例的ServiceCallSite。

   1: internal class TransientCallSite : IServiceCallSite

   2: {

   3:     public IServiceCallSite ServiceCallSite { get; private set; }

   4:     public TransientCallSite(IServiceCallSite serviceCallSite)

   5:     {

   6:         this.ServiceCallSite = serviceCallSite;

   7:     }

   8:  

   9:     public  Expression Build(Expression provider)

  10:     {

  11:         return Expression.Call(provider, "CaptureDisposable", null, this.ServiceCallSite.Build(provider));

  12:     }

  13:  

  14:     public  object Invoke(ServiceProvider provider)

  15:     {

  16:         return provider.CaptureDisposable(this.ServiceCallSite.Invoke(provider));

  17:     }

  18: }

  19:  

  20: internal class ServiceProvider : IServiceProvider, IDisposable

  21: {

  22:     

  23:     public object CaptureDisposable(object instance)

  24:     {

  25:         IDisposable disposable = instance as IDisposable;

  26:         if (null != disposable)

  27:         {

  28:             this.TransientDisposableServices.Add(disposable);

  29:         }

  30:         return instance;

  31:     }

  32:     //其他成员

  33: }

ScopedCallSite在提供服务实例的时候需要考虑当前ServiceProvider的ResolvedServices属性中是否已经存在已经提供的服务实例,如果已经存在它就无需重复创建。新创建的服务实例需要添加到当前ServiceProvider的ResolvedServices属性中。ScopedCallSite定义如下,ServiceCallSite属性依然表示的是真正用于创建服务实力的ServiceCallSite,Service属性则用来获取保存在当前ServiceProvider的ResolvedServices属性中的服务实例。

   1: internal class ScopedCallSite : IServiceCallSite

   2: {

   3:     public IService Service { get; private set; }

   4:     public IServiceCallSite ServiceCallSite { get; private set; }

   5:  

   6:     public ScopedCallSite(IService service, IServiceCallSite serviceCallSite)

   7:     {

   8:         this.Service = service;

   9:         this.ServiceCallSite = serviceCallSite;

  10:     }

  11:  

  12:     public virtual Expression Build(Expression provider)

  13:     {

  14:         var service = Expression.Constant(this.Service);

  15:         var instance = Expression.Variable(typeof(object), "instance");

  16:         var resolvedServices = Expression.Property(provider, "ResolvedServices");

  17:         var tryGetValue = Expression.Call(resolvedServices, "TryGetValue", null, service, instance);

  18:         var index = Expression.MakeIndex(resolvedServices, typeof(ConcurrentDictionary<IService, object>).GetProperty("Item"), new Expression[] { service});

  19:         var assign = Expression.Assign(index, this.ServiceCallSite.Build(provider));

  20:  

  21:         return Expression.Block(typeof(object),new[] { instance },Expression.IfThen(Expression.Not(tryGetValue),assign),index);

  22:     }

  23:  

  24:     public virtual object Invoke(ServiceProvider provider)

  25:     {

  26:         object instance;

  27:         return provider.ResolvedServices.TryGetValue(this.Service, out instance)

  28:             ? instance

  29:             : provider.ResolvedServices[this.Service] = this.ServiceCallSite.Invoke(provider);

  30:     }

  31: }

其实Singleton和Scope这两种模式本质上是等同的,它们表示在某个ServiceScope中的“单例”模式,它们之间的不同之处在于前者的ServiceScope是针对作为根的ServiceProvider,后者则是针对当前ServiceProvider。所以SingletonCallSite是ScopedCallSite的派生类,具体定义如下所示。

   1: internal class SingletonCallSite : ScopedCallSite

   2: {

   3:     public SingletonCallSite(IService service, IServiceCallSite serviceCallSite) : 

   4:     base(service, serviceCallSite)

   5:     { }

   6:  

   7:     public override Expression Build(Expression provider)

   8:     {

   9:         return base.Build(Expression.Property(provider, "Root"));

  10:     }

  11:  

  12:     public override object Invoke(ServiceProvider provider)

  13:     {

  14:         return base.Invoke(provider.Root);

  15:     }

  16: }

三、创建ServiceCallSite

ServiceCallSite的创建体现在IService接口的CreateServiceSite方法中,在我们的Service类这个方法是如何实现的呢?如下面的代码片段所示,真正用于创建服务实例的ServiceCallSite先根据当前的ServiceDescriptor创建出来,然后再根据采用生命周期管理模式创建出与之匹配的ServiceCallSite。

   1: internal class Service : IService

   2: {

   3:     public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)

   4:     {

   5:         IServiceCallSite serviceCallSite = 

   6:             (null != this.ServiceDescriptor.ImplementationInstance)

   7:             ? new InstanceCallSite(this.ServiceDescriptor.ImplementationInstance)

   8:             : null;

   9:  

  10:         serviceCallSite = serviceCallSite?? 

  11:             ((null != this.ServiceDescriptor.ImplementationFactory)

  12:             ? new FactoryCallSite(this.ServiceDescriptor.ImplementationFactory)

  13:             : null);

  14:  

  15:         serviceCallSite = serviceCallSite ?? this.CreateConstructorCallSite(provider, callSiteChain);

  16:  

  17:         switch (this.Lifetime)

  18:         {

  19:             case ServiceLifetime.Transient: return new TransientCallSite(serviceCallSite);

  20:             case ServiceLifetime.Scoped: return new ScopedCallSite(this, serviceCallSite);

  21:             default: return new SingletonCallSite(this, serviceCallSite);

  22:         }

  23:     }

  24:     //其他成员    

  25: }

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): ServiceProvider实现揭秘 【解读ServiceCallSite 】的更多相关文章

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

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

  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. 【专业找水题】状压dp最水题,没有之一

    题目链接 现在代码能力没上升,倒是越来越会找水题了(比例题还水的裸题你值得拥有) 这网站不是针对竞赛的,所以时空限制都很宽松 然后就让我水过去了 对于每个点,包括自己的前m个元素是否取都是一种状态,所 ...

  2. mysql中生产表格多列统计问题

    for Example: select date_format(date,'%Y-%m-%d') as day, count(case when xinghao='a' then 1 end) as  ...

  3. 一些ES5的操作数组的方法

    在ES5规范中新增了不少操作数组的方法,特此罗列一下以备使用 1. forEach循环 有点类似jQuery的each循环 [12,23,36,4,5].forEach(function(v,k){ ...

  4. bootstrap之div居中

    bootstrap之div居中 偏移列 偏移是一个用于更专业的布局的有用功能.它们可用来给列腾出更多的空间.例如,.col-xs=* 类不支持偏移,但是它们可以简单地通过使用一个空的单元格来实现该效果 ...

  5. Torch7学习笔记(一)CmdLine

    该类主要为了提供一种方便解析参数的框架,对于每个实验尤其是神经网络中要调参数上.同时还可以把输出重定向到log文件中. 一般用法: cmd = torch.CmdLine() cmd:text() c ...

  6. / fluxChatDemo / 系列 ——fluxDemoChat 组件编写

    还是用各部分来表示过程吧,没文采,就先这样记着吧 嘻嘻 梳理问题: 编写es6风格的组件时,需要引入import React from ‘react’ 然后页面就华丽丽的展示出了我写的1.2两个字 在 ...

  7. POJ1753 Flip Game(bfs、枚举)

    链接:http://poj.org/problem?id=1753 Flip Game Description Flip game is played on a rectangular 4x4 fie ...

  8. Sublime Text 3 配置和使用方法

    下载: Sublime Text 3 官方下载地址 Sublime Text 3 汉化破解版 资料: Sublime Text 非官方文档   技巧 -用户或-User后缀的菜单项,其对应的配置文件都 ...

  9. JS 怎么控制某个div的滚动条滚动到顶部? (已解决)

    获取这个元素,然后设置它的滚动条的位置为初始位置(0,0). document.getElementById(..).scrollTop = 0;

  10. html 5 实现拖放效果

    在html5中要实现拖放操作,相对于以前通过鼠标操作实现,要简单得多,数据安全性也更有保障.只需要以下几步即可. 给被拖拽元素添加draggable属性,如果是文件拖放. 在拖拽元素的dragstar ...