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 中文乱码解决大全

    源地址:http://blog.csdn.net/xcy2011sky/article/details/7168376 解决中文乱码,最好知道乱码是什么格式比如说:utf-8. 解决方案: 1.让整个 ...

  2. Windows Azure入门教学系列 (三):创建第一个Worker Role程序

    在开始本教学之前,请确保你从Windows Azure 平台下载下载并安装了最新的Windows Azure开发工具.本教学使用Visual Studio 2010作为开发工具. 步骤一:创建解决方案 ...

  3. android studio之argument for @notnull parameter 'name'

    1)进入刚安装的Android Studio目录下的bin目录.找到idea.properties文件,用文本编辑器打开.2)在idea.properties文件末尾添加一行: disable.and ...

  4. Eclipse完美汉化教程

    首先我们打开http://www.eclipse.org/babel/downloads.php下载语言包. 方法一:可以复制图片里的地址通过Eclipse下载,Help→Install New So ...

  5. 第四天学习内容 if switch for 的练习

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...

  6. C#递归复制文件夹

    /// <param name="sources">原路徑</param> /// <param name="dest">目 ...

  7. uva 12096

    优先队列,主要是STL应用所以复制一下 #include <iostream> #include <cstdio> #include <cstdlib> #incl ...

  8. 快速排序算法之我见(附上C代码)

    因为<The C Programming Language>一书中有一个练习,需要用到快速排序,所以又复习了一下,感觉收获颇多,故而分享之. 快速排序的核心是一种 divide and c ...

  9. UVA 529 Addition Chains(迭代搜索)

      Addition Chains  An addition chain for n is an integer sequence  with the following four propertie ...

  10. 第二章 IoC Setter注入

    Setter注入又称为属性注入.是通过属性的setXXX()方法来注入Bean的属性值或依赖对象.由于Setter注入具有可选择性和灵活性高的优点,因此Setter注入是实际应用中最常用的注入方式. ...