通过上一篇的介绍我们应该对实现在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. 异步调用window.open时被浏览器阻止新窗口解决方案

    var wyWindow = window.open('_blank');$http.post($rootScope.baseUrl + '/Interface0231A.ashx', { userF ...

  2. java-PreparedStatement的用法

    转自:http://www.cnblogs.com/raymond19840709/archive/2008/05/12/1192948.html PreparedStatement的用法 jdbc( ...

  3. SQL 表的完整性

    建立:主外键,约束.(删除主表的时候,同时删除子表:更新主表的时候更新子表) 1.建表时定义主键 Create table 表名 ( Sno int identity(1,1), Sname nvar ...

  4. PHP 通过百度API 实现通过城市名称获取经度

    $city = $_GET['city'];print_r(getjw($city));/*** $city 需要查询的地址* $key 百度开发者账号*/function getjw($city){ ...

  5. jquery 回到顶部,简洁大方

    效果

  6. FreeMarker中文API手册(完整)

    FreeMarker概述 FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写 FreeMarker被设计用来生成HTML Web页面,特别是基于MVC模式的应用 ...

  7. .NET C#-- 利用BeginInvoke与EndInvoke完成异步委托方法并获取方法执行返回值示例

    //定义委托 delegate string MyDelegate(string name); //定义委托调用函数 public string Hello(string name) { Thread ...

  8. ubuntu 安装与开始学习

    下载地址 http://cn.ubuntu.com/download/ 经验: 1.遇到安装问题,首先尝试解读错误,再使用  ./configure --help  不行再上Stack overflo ...

  9. ABP理论学习之验证DTO

    返回总目录 本篇目录 验证介绍 使用数据注解 自定义验证 标准化 验证介绍 首先应该验证应用的输入.用户或者其它应用都可以向该应用发送输入.在一个web应用中,验证通常要实现两次:在客户端和服务器端. ...

  10. Nova PhoneGap框架 第二章 理解index.html

    跟绝大多数PhoneGap程序一样,Index.html是程序的入口.这个页面应该完成应用程序的初始化工作. 首先,让我们来看看这个页面通常都长什么样子: 下面我将一一解释这个页面都做了哪些初始化工作 ...