通过上一篇的介绍我们应该对实现在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. js学习笔记

    javacript笔记根据EC5.0一共有六种数据类型:number,string,bool undefine,nullobject(广义的) --->object(狭义的),array,fun ...

  2. 我听说 C...

    我听说在 c 语言的世界里,goto 和异常处理都是声名狼藉的东西,而我认为它们在一起就能化解各自的问题.

  3. java爬虫:在请求body中增加json数据采集

    1,http://www.hqepay.com/public/expressquery.html 查询快递不是将键值对post过去,而是将json数据放到body中发送过去.抓包如下: 2,需要导入一 ...

  4. java基础知识小总结【转】

    java基础知识小总结 在一个独立的原始程序里,只能有一个 public 类,却可以有许多 non-public 类.此外,若是在一个 Java 程序中没有一个类是 public,那么该 Java 程 ...

  5. linux opensuse 程序中上传文件大小限制

    上传文件大小限制 修改nginx主配置文件 /etc/nginx/nginx.conf增加配置 client_max_body_size 40m;  (说明:这里40m是最大支持上传40兆)

  6. Ubuntu下安装mod_python报错(GIT错误)

    Ubuntu下安装mod_python3.4.1版本报出如下错误: writing byte-compilation script '/tmp/tmpE91VXZ.py' /usr/bin/pytho ...

  7. centos7安装mysql5.7

    http://jingyan.baidu.com/album/93f9803f010d8fe0e56f555e.html?picindex=15

  8. Jtable 表格按多列排序(支持中文汉字排序)

    这两天公司让做一个Jtable表格的排序,首先按A列排序,在A列相等时按B列排序,B列相等时按C列排序,ABC三列可以任意指定,最多分三列,这样的一个需求.由于我是大神,所以必须做了出来.ok,不自恋 ...

  9. canvas绘制简易时钟

    时钟绘制的非常简易,但该有的都有了. 效果图如下, <!DOCTYPE html> <html> <head lang="en"> <me ...

  10. SDOI 2016 数字配对

    题目大意:给定n个数字以及每个数字的个数和权值,将满足条件的数字配对,使得总代价不小于0,且配对最多 最大费用最大流拆点,对于每个点,连一条由S到该点的边,容量为b,花费为0,再连一条到T的边 对于每 ...