通过上一篇的介绍我们应该对实现在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. bzoj1503郁闷的出(cheng)纳(xu)员

    好痛苦,,,WA了不知道多少遍 错的服了,,, 如果某员工的初始工资低于工资下界,他将立刻离开公司 我也不知道是我语文有问题还是题目有毒,反正这个东西好像不应该算在离开公司的总人数的答案里... 让我 ...

  2. 多线程、委托、Invoke解决winform界面卡死的问题,并带开关

    一.知识点介绍 1,更新控件的内容,应该调用控件的Invoke方法. Invoke指: 在拥有控件的基础窗口句柄的线程上,用指定的参数列表执行指定委托.该方法接收一个委托类型和委托的参数,因此需要定义 ...

  3. ajax和jquery

    ajax的定义: AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术. AJAX = 异步 Ja ...

  4. mac安装虚拟机

    1. 安装VirtualBox 2. 新建,按照步骤一步步选择 3.安装系统镜像 xp_sp3_74070.iso CN_WIN7_SP1_X64_33in1_V1.2.iso 设置磁盘分区等 4.V ...

  5. 转:CentOS, 找不到dump命令:command not found

    dump 功能说明:备份文件系统.语 法:dump [-cnu][-0123456789][-b <区块大小>][-B <区块数目>][-d <密度>][-f &l ...

  6. jQuery最基础最全面的选择器大览

    一.基本选择器     1.标签选择器 (element):根据给定的标签名匹配元素 eg:$("h2")选取所有h2元素 --- 返回元素集合 2.类选择器 (.class):根 ...

  7. 【完全开源】Django多人博客系统——支持MarkDown和tinyMce

    目录 说明 功能 如何使用 说明 这是一个用Django开发的多人博客系统,功能简单,但完全满足公司内部或个人的博客使用需求.支持普通富文本编辑器(tinyMCE)和MarkDown编辑器 由于嫌弃D ...

  8. SQL——行值表达式(Row Value Expressions)

    概述 最近接触了一个新概念——行值表达式,也叫做行值构造器.这是一个很强大的SQL功能,通常我们所操作的SQL表达式都只能针对一行中的单一字段进行操作比较,而行值表达式可以针对一行中的多个字段进行操作 ...

  9. iOS开发——post异步网络请求封装

    IOS中有许多网络请求的函数,同步的,异步的,通过delegate异步回调的. 在做一个项目的时候,上网看了很多别人的例子,发现都没有一个简单的,方便的异步请求的封装例子.我这里要给出的封装代码,是异 ...

  10. .Net 跨平台可移植类库PCL可用于任何平台包括Mono

    Microsoft 在 .NET Framework 4 中添加了一个名为可移植类库 (PCL) 的新功能. 利用 PCL,您可以有选择性地面向 .NET Framework.Silverlight ...