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

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

一、提供一个ServiceProvider对象

我们知道当将服务类型指定为IServiceProvider接口并调用ServiceProvider的GetService方法是,ServiceProvider对象本身将会作为服务实例返回,这个特性可以利用一个自定义的Service来实现。如下面的代码片段所示,我们定义的这个ServiceProviderService既是一个Service,又是一个ServiceCallSite。它默认采用生命周期管理模式为Scoped,在Invoke和Build方法中,它直接将当前ServiceProvider作为提供的服务实例。在初始化ServiceTable的时候,我们额外添加一个针对ServiceProviderService的ServideEntry。

   1: internal class ServiceProviderService : IService, IServiceCallSite
   2: {

   3:     public ServiceLifetime Lifetime => ServiceLifetime.Scoped;

   4:     public IService Next { get; set; }

   5:  

   6:     public Expression Build(Expression provider)

   7:     {

   8:         return provider;

   9:     }

  10:  

  11:     public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)

  12:     {

  13:         return this;

  14:     }

  15:  

  16:     public object Invoke(ServiceProvider provider)

  17:     {

  18:         return provider;

  19:     }

  20: }

  21:  

  22: internal class ServiceTable

  23: {

  24:     public ServiceTable(IServiceCollection services)

  25:     {

  26:         //解析ServiceCollection并添加相应ServiceEntry

  27:         this.ServieEntries[typeof(IServiceProvider)] = new ServiceEntry(new ServiceProviderService());

  28:     }

  29: }

二、创建ServiceScope

创建ServiceScope的目的在于创建作为当前ServiceProvider儿子的另一个ServiceProvider,新创建的ServiceProvider不仅与原来的ServiceProvider具有相同的根,同时共享所有的服务注册信息。利用这个新的ServiceProvider来代替现有的ServiceProvider,其主要的目的还是使我们能够及时地回收提供的服务实例。ServiceScope是通过它的工厂ServiceScopeFactory来创建的,所以先创建了如下一个ServiceScopeFactory类和对应的ServiceScope,它们的定义与我们在前面一节介绍的完全一致。

   1: internal class ServiceScope : IServiceScope
   2: {

   3:     public IServiceProvider ServiceProvider { get; private set; }

   4:  

   5:     public ServiceScope(ServiceProvider serviceProvider)

   6:     {

   7:         this.ServiceProvider = serviceProvider;

   8:     }

   9:  

  10:     public void Dispose()

  11:     {

  12:         (this.ServiceProvider as IDisposable)?.Dispose();

  13:     }

  14: }

  15:  

  16: internal class ServiceScopeFactory : IServiceScopeFactory

  17: {

  18:     public ServiceProvider ServiceProvider { get; private set; }

  19:  

  20:     public ServiceScopeFactory(ServiceProvider serviceProvider)

  21:     {

  22:         this.ServiceProvider = serviceProvider;

  23:     }

  24:  

  25:     public IServiceScope CreateScope()

  26:     {

  27:         return new ServiceScope(this.ServiceProvider);

  28:     }

  29: }

  30:  

  31: internal class ServiceProvider : IServiceProvider, IDisposable

  32: {

  33:     

  34:     public ServiceProvider(ServiceProvider parent)

  35:     {

  36:         this.Root = parent.Root;

  37:         this.ServiceTable = parent.ServiceTable;

  38:     }

  39: }

为了让ServiceProvider的GetService方法在服务类型指定为IServiceScopeFactory接口的时候能够自动返回上面我们定义的ServiceScopeFactory对象,我们依然和上面一样创建了一个自定义的Service,并将其命名为ServiceScopeFactoryService。与ServiceProviderService一样,ServiceScopeFactoryService同时也是一个ServiceCallSite,在Build和Invoke方法中它会返回一个ServiceScopeFactory对象。为了让这个它能够生效,我们依然在ServiceTable初始化的时自动添加一个相应的ServiceEntry。

   1: internal class ServiceScopeFactoryService : IService, IServiceCallSite
   2: {

   3:     public ServiceLifetime Lifetime=> ServiceLifetime.Scoped;

   4:     public IService Next { get; set; }

   5:  

   6:     public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain)

   7:     {

   8:         return this;

   9:     }

  10:  

  11:     public Expression Build(Expression provider)

  12:     {

  13:         return Expression.New(typeof(ServiceScopeFactory).GetConstructors().Single(), provider);

  14:     }

  15:  

  16:     public object Invoke(ServiceProvider provider)

  17:     {

  18:         return new ServiceScopeFactory(provider);

  19:     }

  20: }

  21:  

  22: internal class ServiceTable

  23: {

  24:     public ServiceTable(IServiceCollection services)

  25:     {

  26:         //解析ServiceCollection并添加相应ServiceEntry

  27:         this.ServieEntries[typeof(IServiceProvider)] =  new ServiceEntry(new ServiceProviderService());

  28:         this.ServieEntries[typeof(IServiceScopeFactory)] = new ServiceEntry(new ServiceScopeFactoryService());

  29:     }

  30: }

三、提供一组服务的集合

到目前为止,我们自定义的ServiceProvider尚不具备原生ServiceProvider的一项特性,那就是当调用GetService方法时将服务类型指定为IEnumerable<T>或者直接调用扩展方法GetServices时,得到的是一个服务实例的集合。这个特性可以通过一个自定义的ServiceCallSite来完成,我们将其命名为EnumerableCallSite。

   1: internal class EnumerableCallSite : IServiceCallSite
   2: {

   3:     public Type ElementType { get; private set; }

   4:     public IServiceCallSite[] ServiceCallSites { get; private set; }

   5:  

   6:     public EnumerableCallSite(Type elementType, IServiceCallSite[] serviceCallSites)

   7:     {

   8:         this.ElementType = elementType;

   9:         this.ServiceCallSites = serviceCallSites;

  10:     }

  11:  

  12:     public Expression Build(Expression provider)

  13:     {

  14:         return Expression.NewArrayInit(this.ElementType, this.ServiceCallSites.Select(

  15:             it => Expression.Convert(it.Build(provider), this.ElementType)));

  16:     }

  17:  

  18:     public object Invoke(ServiceProvider provider)

  19:     {

  20:         var array = Array.CreateInstance(this.ElementType, this.ServiceCallSites.Length);

  21:         for (var index = 0; index < this.ServiceCallSites.Length; index++)

  22:         {

  23:             array.SetValue(this.ServiceCallSites[index].Invoke(provider), index);

  24:         }

  25:         return array;

  26:     }

  27: }

如上面的代码片段所示,EnumerableCallSite具有两个两个只读属性(ElementType和ServiceCallSites),前者表示返回的服务集合的元素类型,后者则返回一组用于提供集合元素的ServiceCallSite。在Invoke和Build方法中,我们只需要根据元素类型创建一个数组,并利用这组ServiceCallSite创建所有的元素即可。这个EnumerableCallSite最终按照如下的方式应用到ServiceProvider的GetServiceCallSite方法中。

   1: internal class ServiceProvider : IServiceProvider, IDisposable
   2: { 

   3:     public IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain)

   4:     {

   5:         try

   6:         {

   7:             if (callSiteChain.Contains(serviceType))

   8:             {

   9:                 throw new InvalidOperationException(string.Format("A circular dependency was detected for the service of type ‘{0}‘",serviceType.FullName);

  10:             }

  11:             callSiteChain.Add(serviceType);

  12:             ServiceEntry serviceEntry;

  13:             if (this.ServiceTable.ServieEntries.TryGetValue(serviceType, out serviceEntry))

  14:             {

  15:                 return serviceEntry.Last.CreateCallSite(this, callSiteChain);

  16:             }

  17:  

  18:             if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition()== typeof(IEnumerable<>))

  19:             {

  20:                 Type elementType = serviceType.GetGenericArguments()[0];

  21:                 IServiceCallSite[] serviceCallSites = this.ServiceTable.ServieEntries.TryGetValue(elementType, out serviceEntry)

  22:                     ? serviceEntry.All.Select(it => it.CreateCallSite(this, callSiteChain)).ToArray()

  23:                     : new IServiceCallSite[0];

  24:                 return new EnumerableCallSite(elementType, serviceCallSites);

  25:             }

  26:  

  27:             return null;

  28:         }

  29:         finally

  30:         {

  31:             callSiteChain.Remove(serviceType);

  32:         }

  33:     }

  34:     //其他成员

  35: }

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实现揭秘【补充漏掉的细节】

时间: 2024-10-24 19:03:32

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

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

通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的.ServiceProvider最终采用何种方式提供我们所需的服务实例取决于最终选择了怎样的ServiceCallSite,而服务注册是采用的ServiceDescriptor有决定了ServiceCallSite类型的选择.我们将众多不同类型的ServiceCallSite大体分成两组,一组用来创建最终的服务实例,另一类则与生命周

ASP.NET Core中的依赖注入(2):依赖注入(DI)

参考页面: http://www.yuanjiaocheng.net/ASPNET-CORE/project-layout.html http://www.yuanjiaocheng.net/ASPNET-CORE/projectjson.html http://www.yuanjiaocheng.net/ASPNET-CORE/core-configuration.html http://www.yuanjiaocheng.net/ASPNET-CORE/core-middleware.htm

ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】

本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还对这个依赖注入系统底层的实现原理具有好奇心,可以继续阅读这一节的内容. 目录一.ServiceCallSite 二.Service 三.ServiceEntry 四.ServiceTable 五.ServiceProvider 作为DI容器的体现,ServiceProvider是ASP.NET Co

ASP.NET Core 中的依赖注入 [共7篇]

一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了“标准化”,我们将这些标准化的组件称为服务,ASP.NET在内部专门维护了一个DI容器来提供所需的服务.要了解这个DI容器以及现实其中的服务提供机制,我们先得知道什么是DI(Dependence Injection),而一旦我们提到DI,又不得不说IoC(Inverse of Control)… [

ASP.NET Core中的依赖注入(1):控制反转(IoC)

ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化",我们将这些标准化的组件称为服务,ASP.NET在内部专门维护了一个DI容器来提供所需的服务.要了解这个DI容器以及现实其中的服务提供机制,我们先得知道什么是DI(Dependence Injection),而一旦我们提到DI,又不得不说IoC(Inverse of Control). 目录 一.流程控

深入理解net core中的依赖注入、Singleton、Scoped、Transient(四)

相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient(二) 深入理解net core中的依赖注入.Singleton.Scoped.Transient(三) 一.什么是依赖注入(Denpendency Injection) 这也是个老身常谈的问题,到底依赖注入是什么? 为什么要用它? 初学者特别容易对控制反转IOC(Iversion of Contr

深入理解net core中的依赖注入、Singleton、Scoped、Transient(一)

相关文章: 深入理解net core中的依赖注入.Singleton.Scoped.Transient(一) 深入理解net core中的依赖注入.Singleton.Scoped.Transient(二) 深入理解net core中的依赖注入.Singleton.Scoped.Transient(三) 深入理解net core中的依赖注入.Singleton.Scoped.Transient(四) 在学习 Asp.Net Core 的过程中,注入可以说是无处不在,对于 .Net Core 来说

asp.net core 系列 3 依赖注入

一. 依赖注入概述 在软件设计的通用原则中,SOLID是非常流行的缩略语,它由5个设计原则的首字母构成:单一原则(S).开放封闭原则(O).里氏替换原则(L).接口分离原则(I).依赖反转原则(D).本篇介绍依赖反转原则以及在ASP.NET Core中的实现. 直接依赖是指:当一个类需要另一个类协作来完成工作的时候就产生了依赖.举例比如:模块 A 调用模块 B 中的函数,而模块 B 又调用模块 C 中的函数,则编译时 A 取决于 B,而 B 又取决于 C.这是有严重的依赖关系,不属于松散耦合.

ASP.NET Core MVC 之依赖注入 Controller

ASP.NET Core MVC 控制器应通过构造函数明确地请求它们地依赖关系,在某些情况下,单个控制器地操作可能需要一个服务,在控制器级别上的请求可能没有意义.在这种情况下,也可以将服务作为  Action 的参数. 依赖注入是一种如 Dependency Inversion Principle 所示的技术,允许应用程序松散耦合的模块组成. 1.构造函数注入 ASP.NET Core 内置的基于构造函数的依赖注入支持扩展到 MVC 控制器.通过只添加一个服务类型作为构造函数参数到控制器中,AS