我们知道整个ASP.NET Core建立在以ServiceCollection/ServiceProvider为核心的DI框架上,它甚至提供了扩展点使我们可以与第三方DI框架进行整合。对此比较了解的读者朋友应该很清楚,针对第三方DI框架的整合可以通过在定义Startup类型的ConfigureServices方法返回一个ServiceProvider来实现。但是真的有这么简单吗?

一、ConfigureServices方法返回的ServiceProvider貌似没有用!?

我们可以通过一个简单的实例来说明这个问题。我们先定义了如下这个一个MyServiceProvider,它实际上是对另一个ServiceProvider的封装。简单起见,我们利用一个字典来保存服务接口与实现类型的映射关系,这个关系可以通过调用Registe方法来注册。在提供服务实例的GetService方法中,如果提供的服务类型已经被注册,我们会创建并返回对应的实例对象,否则我们将利用封装的这个ServiceProvider来提供服务。为了确保服务实例能够被正常回收,如果服务类型实现了IDisposable接口,我们会将它添加到通过字段_disposables表示的集合中。当MyServiceProvider的Dispose方法被调用的时候,提供的这些服务实例的Dispose方法会被调用。

   1: public class MyServiceProvider : IServiceProvider, IDisposable

   2: {

   3:     private IServiceProvider        _innerServiceProvider;

   4:     private Dictionary<Type, Type>  _services;

   5:     private List<IDisposable>       _disposables;

   6:  

   7:     public MyServiceProvider(IServiceProvider innerServiceProvider)

   8:     {

   9:         _innerServiceProvider  = innerServiceProvider;

  10:         this._services         = new Dictionary<Type, Type>();

  11:         _disposables           = new List<IDisposable>();

  12:     }

  13:  

  14:  

  15:     public MyServiceProvider Register<TFrom, TTo>() where TTo: TFrom, new()

  16:     {

  17:         _services[typeof(TFrom)] = typeof(TTo);

  18:         return this;

  19:     }

  20:  

  21:     public object GetService(Type serviceType)

  22:     {

  23:         Type implementation;

  24:         if (_services.TryGetValue(serviceType, out implementation))

  25:         {

  26:             object service = Activator.CreateInstance(implementation);

  27:             IDisposable disposbale = service as IDisposable;

  28:             if (null != disposbale)

  29:             {

  30:                 _disposables.Add(disposbale);

  31:             }

  32:             return service;

  33:         }

  34:         return _innerServiceProvider.GetService(serviceType);

  35:     }

  36:  

  37:     public void Dispose()

  38:     {

  39:         (_innerServiceProvider as IDisposable)?.Dispose();

  40:         foreach (var it in _disposables)

  41:         {

  42:             it.Dispose();

  43:         }

  44:         _disposables.Clear();

  45:     }

  46: }

我们按照如下的方式在一个ASP.NET Core应用中使用MyServiceProvider。如下面的代码片断中,在注册的Starup类型中,我们让ConfigureServices方法返回一个MyServiceProvider对象。服务接口IFoobar和实现类型Foobar之间的映射注册在这个MyServiceProvider对象上。在处理请求的时候,我们利用当前HttpContext对象的RequestServices属性得到为请求处理提供服务的ServiceProvider,并试图利用它得到注册的IFoobar服务。

   1: public class Program

   2: {

   3:     public static void Main(string[] args)

   4:     {

   5:         new WebHostBuilder()

   6:             .UseKestrel()

   7:             .UseStartup<Startup>()

   8:             .Build()

   9:             .Run();

  10:     }

  11: }

  12:  

  13: public class Startup

  14: {

  15:     public IServiceProvider ConfigureServices(IServiceCollection services)

  16:     {

  17:         return new MyServiceProvider(services.BuildServiceProvider())

  18:             .Register<IFoobar, Foobar>();

  19:     }

  20:  

  21:     public void Configure(IApplicationBuilder app)

  22:     {

  23:         app.UseDeveloperExceptionPage()

  24:             .Run(async context => await context.Response.WriteAsync(context.RequestServices.GetRequiredService<IFoobar>().GetType().Name));

  25:     }

  26: }

  27: public interface IFoobar { }

  28: public class Foobar : IFoobar { }

整个应用就这样简单,貌似也没有什么问题,但是我们启动应用并利用浏览器访问该应用是就会出现如下所示的错误。错误信息表示服务接口IFoobar尚未被注册。

二、原因何在?

我们明明在返回的ServiceProvider注册了IFoobar和Foobar之间的映射关系,为什么RequestServices返回的ServiceProvider说该服务尚未被注册呢?唯一的解释就是ConfigureServices方法返回的ServiceProvider与HttpContext的RequestServices返回的ServiceProvider根本就不是同一个。实际上它们本来就不是同一个对象。

我在《从两个不同的ServiceProvider说起》中曾经谈到过:ConfigureServices方法返回的ServiceProvider将会作为WebHost的ServiceProvider,对于每次接收的请求,WebHost会根据这个ServiceProvider创建一个新的ServiceProvider来作为HttpContext的RequestServices属性,这两个ServiceProvider具有父子管理。照例说,如果RequestServices返回的ServiceProvider是根据ConfigureServices方法返回的ServiceProvider创建的,那么它也应该能够识别注册的服务类型IFoobar,那么为什么依然会出现错误呢?

要了解这个问题,就需要知道这个所谓的“子ServiceProvider”是如何被创建出来的,这其中涉及到ServiceScope的概念。简单来说,ServiceScope是对一个ServiceProvider的封装,前者决定后者的生命周期。ServiceScope由ServiceScopeFactory创建,后者以一个服务的形式注册到“父ServiceProvider”上面。当“父ServiceProvider”需要创建“子ServiceProvider”的时候,它会调用GetService方法得到这个ServiceScopeFactory对象(采用的服务接口为IServiceScopeFactory),并利用后者创建一个ServiceScope,这个ServiceScope提供的ServiceProvider就是返回的“子ServiceProvider”。

但是对于我们的MyServiceProvider对象来说,当调用它的GetService方法试图获取ServiceScopeFactory对象的时候,获取的实际上是被封装的那个SerivceProvider关联的ServiceScopeFactory,那么很自然创建的“子ServiceProvider”也与MyServiceProvider没有什么关系。

三、如何解决这个问题?

既然我们知道了问题的根源,我们自然就有了解决方案。解决方案并不复杂,我们只需要MyServiceProvider的GetService方法返回反映其自身服务注册相关的ServiceScopeFactory。为此我们定义了如下一个ServiceScope和对应的ServiceScopeFactory。

   1: internal class ServiceScope : IServiceScope

   2: {

   3:     private MyServiceProvider _serviceProvider;

   4:  

   5:     public ServiceScope(IServiceScope innserServiceScope, Dictionary<Type, Type> services)

   6:     {

   7:         _serviceProvider = new MyServiceProvider(innserServiceScope.ServiceProvider, services);

   8:     }

   9:     public IServiceProvider ServiceProvider

  10:     {

  11:         get { return _serviceProvider; }

  12:     }

  13:  

  14:     public void Dispose()

  15:     {

  16:         _serviceProvider.Dispose();

  17:     }

  18: }

  19:  

  20: internal class ServiceScopeFactory : IServiceScopeFactory

  21: {

  22:     private IServiceScopeFactory _innerServiceFactory;

  23:     private Dictionary<Type, Type> _services;

  24:  

  25:     public ServiceScopeFactory(IServiceScopeFactory innerServiceFactory, Dictionary<Type, Type> services)

  26:     {

  27:         _innerServiceFactory = innerServiceFactory;

  28:         _services = services;

  29:     }

  30:     public IServiceScope CreateScope()

  31:     {

  32:         return new ServiceScope(_innerServiceFactory.CreateScope(), _services);

  33:     }

  34: }

除此之外,我们为MyServiceProvider添加了一个构造函数,GetService方法也针对IServiceScopeFactory添加了相应的代码。

   1: public class MyServiceProvider : IServiceProvider, IDisposable

   2: {

   3:     public MyServiceProvider(IServiceProvider innerServiceProvider, Dictionary<Type, Type> services)

   4:     {

   5:         _innerServiceProvider = innerServiceProvider;

   6:         _services = services;

   7:         _disposables = new List<IDisposable>();

   8:     }

   9:  

  10:     public object GetService(Type serviceType)

  11:     {

  12:         if (serviceType == typeof(IServiceScopeFactory))

  13:         {

  14:             IServiceScopeFactory innerServiceScopeFactory = _innerServiceProvider.GetRequiredService<IServiceScopeFactory>();

  15:             return new ServiceScopeFactory(innerServiceScopeFactory, _services);

  16:         }

  17:         ...        

  18:     }

  19:     ...

  20: }

如何在ASP.NET Core应用中实现与第三方IoC/DI框架的整合?的更多相关文章

  1. NET Core应用中实现与第三方IoC/DI框架的整合?

    NET Core应用中实现与第三方IoC/DI框架的整合? 我们知道整个ASP.NET Core建立在以ServiceCollection/ServiceProvider为核心的DI框架上,它甚至提供 ...

  2. 如何在 ASP.NET Core 测试中操纵时间?

    有时候,我们会遇到一些跟系统当前时间相关的需求,例如: 只有开学季才允许录入学生信息 只有到了晚上或者周六才允许备份博客 注册满 3 天的用户才允许进行一些操作 某用户在 24 小时内被禁止发言 很显 ...

  3. ASP.NET Core SignalR中的流式传输

    什么是流式传输? 流式传输是这一种以稳定持续流的形式传输数据的技术. 流式传输的使用场景 有些场景中,服务器返回的数据量较大,等待时间较长,客户端不得不等待服务器返回所有数据后,再进行相应的操作.这时 ...

  4. 在 ASP.NET Core 项目中使用 AutoMapper 进行实体映射

    一.前言 在实际项目开发过程中,我们使用到的各种 ORM 组件都可以很便捷的将我们获取到的数据绑定到对应的 List<T> 集合中,因为我们最终想要在页面上展示的数据与数据库实体类之间可能 ...

  5. 在 ASP.NET Core 项目中使用 MediatR 实现中介者模式

    一.前言  最近有在看 DDD 的相关资料以及微软的 eShopOnContainers 这个项目中基于 DDD 的架构设计,在 Ordering 这个示例服务中,可以看到各层之间的代码调用与我们之前 ...

  6. 采用最简单的方式在ASP.NET Core应用中实现认证、登录和注销

    在安全领域,认证和授权是两个重要的主题.认证是安全体系的第一道屏障,是守护整个应用或者服务的第一道大门.当访问者请求进入的时候,认证体系通过验证对方的提供凭证确定其真实身份.认证体系只有在证实了访问者 ...

  7. 如何在ASP.NET Core中实现CORS跨域

    注:下载本文的完整代码示例请访问 > How to enable CORS(Cross-origin resource sharing) in ASP.NET Core 如何在ASP.NET C ...

  8. 如何在ASP.NET Core中实现一个基础的身份认证

    注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...

  9. 如何在ASP.NET Core中应用Entity Framework

    注:本文提到的代码示例下载地址> How to using Entity Framework DB first in ASP.NET Core 如何在ASP.NET Core中应用Entity ...

随机推荐

  1. 原生AJAX基础讲解及兼容处理

    原文:原生AJAX基础讲解及兼容处理 AJAX = Asynchronous JavaScript and XML (异步的JavaScript和XML). AJAX不是新技术 ,但却是热门的技术.它 ...

  2. mysql utf8mb4与emoji表情

    一 什么是Emoji emoji就是表情符号:词义来自日语(えもじ,e-moji,moji在日语中的含义是字符) 表情符号现已普遍应用于手机短信和网络聊天软件. emoji表情符号,在外国的手机短信里 ...

  3. C++在const用法

    注意 const对象默觉得文件的局部变量 在全局作用域里定义非const变量时,它在整个程序中都能够訪问.我们能够把一个非const变量定义在一个文件里,如果已经做了合适的声明,就能够在另外的文件里使 ...

  4. 使用八种牛云存储解决方案ios7.1的app部署问题

    使用八种牛云存储解决方案ios7.1的app部署问题 一个.问题叙述性说明 开发完ios版本号的app.须要将.ipa文件和.plist文件打包上传,供用户下载,在线安装.用户安装过程简单描写叙述例如 ...

  5. Elegant Box主题wpdb::prepare() 报错[已解决]

    整理书签,发现个网页,是解决Elegant Box主题与新版Wordpress3.5主题不兼容的(3.5改了一个函数的参数个数所致).记得当时使用NeoEase出的主题的时候两次遇到这个问题,费了点功 ...

  6. VMWare 11安装操作系统 - 初学者系列 - 学习者系列文章

    在2010年的时候,我写过一篇关于VMWare的安装操作系统的博文.但是今天在QQ群里有人问起VMWare安装操作系统的问题,虽然回答了,但是回头看了下当时那篇博文,决定重新写一文. 首先要获取VMW ...

  7. 开发环境准备:Ruby on Rails开发环境配置

    开发环境准备:Ruby on Rails开发环境配置 前情回顾 上次讲到Vmware虚拟机的安装配置以及Scientific Linux 6.X系统的安装.这回我们的主要任务是在Linux操作系统上完 ...

  8. Asp.Net MVC页面静态化功能实现一:利用IHttpModule,摒弃ResultFilter

    上一篇有提到利用IHttpModule和ResultFilter实现页面静态化功能.后来经过一些改动,将ResultFilter中要实现的功能全部转移到IHttpModule中来实现 Asp.Net ...

  9. 关于WCF的一些知识点

    首先,WCF和WebService一些区别1,WCF支持多中通信协议,http/https tcp/udp/msmq.命名管道,对等网,消息可达性,事物流等.2,WCF可以与ASP.NET集成,共享同 ...

  10. Introducing ASP.NET vNext and MVC 6

    [译]Introducing ASP.NET vNext and MVC 6 原文:http://www.infoq.com/news/2014/05/ASP.NET-vNext?utm_source ...