我们知道整个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. 开源的.Net ORM微型框架SuperHelper

    SuperHelper——灵活通用的.开源的.Net ORM微型框架 SuperHelper是博主利用业余时间编写的一个ORM微型框架,除了可以提高开发效率,与其它ORM框架相比,博主更加喜欢Supe ...

  2. [译]Java中的继承 VS 组合

    (文章翻译自Inheritance vs. Composition in Java) 这篇文章阐述了Java中继承和组合的概念.它首先给出了一个继承的例子然后指出怎么通过组合来提高继承的设计.最后总结 ...

  3. Asp.Net Identity 深度解析 之 注册登录的扩展

    关于权限每个系统都有自己的解决方案,今天我们来讨论一下微软的权限框架Asp.Net Identity ,介绍如下  http://www.asp.net/identity 这里不在赘余. 很多人认为 ...

  4. SSIS如何引用外部DLL

    原文:SSIS如何引用外部DLL 当SSIS引用外部的DLL时,外部的DLL须满足以下条件: 1. DLL是强命名. 2. 加入到GAC (C:\WINDOWS\assembly),直接把DLL拉进目 ...

  5. Mysql高级之主从复制

    原文:Mysql高级之主从复制 主从复制不就是多台服务器嘛!,一个改变另一个也改变啦,内容其实都一样! 原理: 对数据库进行操作会生成一个文件,binlog(二进制文件),从服务器配置relaylog ...

  6. 使用Java编写的B*算法

    package rpg.stage.path; import java.util.ArrayList; import java.util.HashSet; import java.util.Itera ...

  7. 最初的ajax案例

    ----------------------原始的ajax: function guo() {            var xhr            if (XMLHttpRequest) {  ...

  8. Gerrit代码Review入门实战

    代码审核(Code Review)是软件研发质量保障机制中非常重要的一环,但在实际项目执行过程中,却因为种种原因被Delay甚至是忽略.在实践中,给大家推荐一款免费.开放源代码的代码审查软件Gerri ...

  9. CSLA .NET是一个.NET软件开发框架

    CSLA .NET是一个.NET软件开发框架,帮助开发者“为Windows.Web.面向服务和工作流等应用构建强大和可维护的业务逻辑层”. CSLA是Component-based, Scalable ...

  10. vj1010:高精乘+细心模拟

    这题的话思路挺简单的,主要是打一个高精乘,然后考虑一些细节的东西 码得挺少时间的,但是调错调了很久... 讲一下思路吧: 就是读入的时候,先把小数点去掉,mark一下小数点的位置 去掉小数点之后也就进 ...