ServicePrvider实现揭秘
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实现揭秘【补充漏掉的细节】
ServicePrvider实现揭秘的更多相关文章
- ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】
到目前为止,我们定义的ServiceProvider已经实现了基本的服务提供和回收功能,但是依然漏掉了一些必需的细节特性.这些特性包括如何针对IServiceProvider接口提供一个Service ...
- ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】
本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...
- ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【解读ServiceCallSite 】
通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的.ServiceProvider最 ...
- ASP.NET Core中的依赖注入(1):控制反转(IoC)
ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...
- ASP.NET Core中的依赖注入(2):依赖注入(DI)
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...
- ASP.NET Core中的依赖注入(3): 服务的注册与提供
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...
- ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...
- ASP.NET Core 中的依赖注入 [共7篇]
一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了 ...
- NET Core 中的依赖注入
NET Core 中的依赖注入 [共7篇] 一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制, ...
随机推荐
- JSP 的9个内置对象
JSP内置对象:我们在使用JSP进行页面编程时可以直接使用而不需自己创建的一些Web容器已为用户创建好的JSP内置对象.如request,session,response,out等. 下面就JSP2. ...
- 基于visual Studio2013解决面试题之0604O(1)时间复杂度删除链表节点
题目
- ubuntu 安装maven提示出错 The program 'mvn' can be found in the following packages
问题: I am trying to install apache maven 3 in Ubuntu 12.04 lts. What I did was open the terminal then ...
- EL表达式(2)
本篇介绍EL表达式的隐式对象,如同JSP一样,EL也封装了11个隐式对象,通过这些隐式对象可以在EL表达式中直接使用. 在使用EL时,其实EL是先看标识符是否是其隐式对象之一,如果不是,才从四个域(p ...
- Servlet过滤器——日志记录过滤器
1.概述 在实际的项目开发过程中,经常需要在项目运行时,记录并在控制台中输出运行时的日志信息,便于查看项目的运行状况.本实例将介绍如何应用过滤器实现日志记录.运行本实例,将在控制台中输出项目运行时的日 ...
- 基于visual Studio2013解决面试题之0309左移递减序列搜索
题目
- css hack 如何区分 ie7 ie8
.style { width:100px; /*火狐以及一般浏览器*/ width:200px\9; /*IE8*/ *width:150px; /*IE7*/ _width:50px; /*IE6* ...
- 【安卓】eclipse中不可错过的几个秘密、!
1.PackageExplorer显示文件层次的默认方式是平行列出全部包,事实上也可显示成多级,并且效果比navigator好多了. PackageExplorer视图中,"右上角箭头→pa ...
- Lucene.Net 2.3.1开发介绍 —— 二、分词(三)
原文:Lucene.Net 2.3.1开发介绍 -- 二.分词(三) 1.3 分词器结构 1.3.1 分词器整体结构 从1.2节的分析,终于做到了管中窥豹,现在在Lucene.Net项目中添加一个类关 ...
- Lucene.Net 2.3.1开发介绍 —— 一、接触Lucene.Net
原文:Lucene.Net 2.3.1开发介绍 -- 一.接触Lucene.Net 1.引用Lucene.Net类库找到Lucene.Net的源代码,在“C#\src\Lucene.Net”目录.打开 ...