ServiceCallSite

通过上一篇的介绍我们应该对实现在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实现揭秘【补充漏掉的细节】

ServiceCallSite的更多相关文章

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

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

  2. Core官方DI解析(3)-ServiceCallSite.md

    上一篇说过在整个DI框架中IServiceProviderEngine是核心,但是如果直接看IServiceProviderEngine派生类其实看不出也没什么东西,因为这个类型其实都是调用的其它对象 ...

  3. Net6 DI源码分析Part4 CallSiteFactory ServiceCallSite

    Net6 CallSiteFactory ServiceCallSite, CallSiteChain abstract class ServiceCallSite ServiceCallSite是个 ...

  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中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】

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

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

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

随机推荐

  1. QT对话框中show和exec的区别

    转自:http://hi.baidu.com/wangjuns8/blog/item/24b382460dd1c1338694737d.html QDialog的显示有两个函数show()和exec( ...

  2. 【测试】这是用微软word发布的博客

    参考文章: http://blog.163.com/dsp163@126/blog/static/795585732011573383290/ 试试图片, 美女镇楼:    

  3. delpi中的RTTI初试

    java中的反射机制使我们能够在运行期间获取运行期类的信息,那么在delphi中有没有这样的功能呢?答案是有,实现这种功能的机制在delphi中叫做RTTI,废话少说,先来一段demo: 1.先定义一 ...

  4. 开始AFNetworking

    郝萌主倾心贡献,尊重作者的劳动成果,请勿转载. 假设文章对您有所帮助.欢迎给作者捐赠.支持郝萌主,捐赠数额任意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 This ...

  5. Lucene.Net 2.3.1开发介绍 —— 二、分词(一)

    原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(一) Lucene.Net中,分词是核心库之一,当然,也可以将它独立出来.目前Lucene.Net的分词库很不完善,实际应用价值不高.唯 ...

  6. c vs c++ in strcut and class

    c vs c++ in strcut and class 总习惯用c的用法,现在学习C++,老爱拿来比较.声明我用的是g++4.2.1 SUSE Linux.看例子吧 #include <ios ...

  7. 特么的. 最终把 amobbs 的站长阿莫(莫进明)给回骂了一顿.

    起因:  假设你居住的地方,要上马PX等高污染的项目,你会怎么做. 鼓动别人上街暴力示威与军警对抗. 自己待在家里支持怂恿. 这样的人真心猥琐! 鉴于他常常私自改动帖子, 在此截图留存. 真特么没劲. ...

  8. Swift - 页控件(UIPageControl)的用法

    使用页控件可以用来展示多个桌面.比如很多应用第一次登陆时,会在开始页面使用页控件来介绍功能,通过左右滑动来切换页. 通常我们使用UIPageControl和UIScrollView相互结合来实现多页切 ...

  9. 使用GDI+绘制的360风格按钮控件(使用CN_DRAWITEM消息重绘,并使用TGPGraphics,TGPPen,TGPImage,TGPBitmap等)good

    将下面的代码拷贝到一个单元中,创建一个包,加入这个单元后安装.使用的时候设置好背景颜色,边框颜色,图标(png格式)相对路径的文件名称.这个控件可以利用PNG图像的颜色透明特性,背景色默认透明度为50 ...

  10. spring开发基础

    Spring是一个开源框架,它由Rod Johnson创建.它是为了解决企业应用开发的复杂性而创建的.Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Spring的用途 ...