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

news2024/11/19 21:29:29

我们知道整个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根本就不是同一个。实际上它们本来就不是同一个对象。

我在前面中曾经谈到过: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: }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/653678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大学毕业生自荐信范文

大学毕业生自荐信范文1 尊敬的贵公司领导&#xff1a; 您好&#xff01; 首先感谢您对我的关注&#xff0c;在我即将走出校门的时候&#xff0c;给我一次宝贵的工作机会。 学习了两年的会计专业相关知识后&#xff0c;已经使得我对会计行业有了深刻的认识。技能要靠经验&#x…

7.OpenCV-图像轮廓

1.通过OpenCV的findContours可以很方便的找到图片中内容的轮廓。 2.为了提取轮廓有更高的准确率&#xff0c;在提取轮廓前&#xff0c;需要对图片进行预处理&#xff08;二值阈值&#xff09;&#xff0c;只保留感兴趣的图像。 3.通过计算轮廓面积与外接矩形或外接圆的面积&a…

2023年6/16 操作系统小知识点

目录 分值第二章1. 进程状态转化的条件2. 进程控制块概念&#xff0c;作用&#xff0c;构成。3. 进程上下文概念&#xff0c;组成4. 进程的创建过程以及步骤5. 作业调度和低级调度算法&#xff0c;基本思想&#xff0c;7个&#xff0c;都要掌握6. 线程概念和处理过程7. 为什么有…

【玩转Docker小鲸鱼叭】虚拟化技术是什么?

到底什么是虚拟化&#xff1f; Docker 是一款基于容器虚拟化技术构建的软件&#xff0c;那到底什么虚拟化技术呢&#xff1f;在学习Docker之前&#xff0c;先简单了解下虚拟化技术。 虚拟化是云原生的实现基础&#xff0c;它能够帮助我们更加有效地利用物理计算机硬件。 虚拟…

德国Kipu Quantum与法国Pasqal合作优化量子算法

​ &#xff08;图片来源&#xff1a;网络&#xff09; 6月12日&#xff0c;德国量子软件公司Kipu Quantum宣布与法国中性原子量子计算领域的领导者PASQAL达成为期两年的研发协议&#xff0c;以优化量子算法功能&#xff0c;并为企业加速带来量子效益。 Kipu Quantum专注于为特…

Pytest教程__allure报告(11)

一、allure工具环境配置 windows安装allure 1、下载allure工具包 进入工具包官网&#xff1a;https://github.com/allure-framework/allure2/releases 2、解压下载包 3、将解压包的bin目录路径添加到环境变量中 4、打开cmd命令行输入 allure --version 确认是否安装成功 lin…

文件从 UNIX(LF) 批量改为 PC(CR+LF) ,编码格式保持源文件编码,CMD转换成功

如何把文件从 UNIX(LF) 批量改为 PC(CRLF) &#xff0c;编码格式保持源文件编码&#xff0c;通过电脑自带cmd 批量更改-1.0 chcp 65001 && FOR /F "tokens*" %f IN (dir /b D:\opt\output\DATA_FILE\20230531\*.DAT) DO type "D:\opt\output\DATA_FILE…

经典的设计模式21——策略模式

文章目录 策略模式 猛的发现策略模式和状态模式的结构图长得到好像&#xff0c;可得好好区分区分。 不过真的好像&#xff0c;就是方法那里传递的参数不一样。 百度来一波。 策略模式 定义&#xff1a; 定义了算法家族&#xff0c;分别封装起来&#xff0c;让他们之间可以互…

数字电路基础---锁存器

目录 锁存器 1、简介 2、实验任务 3、程序设计 3.1、缺少 else 分支的锁存器代码 3.2、补齐 else 分支 3.3、缺少 default 的 case 语句的锁存器代码 3.3、补齐 default 的 case 语句 4、本章总结 锁存器 锁存器&#xff08;俗称 Latch&#xff09;是数字电路中的一种…

别让存储成为ChatGPT大模型训练的关键瓶颈

当下科技行业be like... 据说现在的科技公司&#xff0c;不是在抢GPU&#xff0c;就是在往抢GPU的路上……此前4月&#xff0c;特斯拉CEO马斯克就购买了1万块GPU&#xff0c;他还称公司将继续大量购买英伟达的GPU。 在国内&#xff0c;近日也有报道称&#xff0c;字节跳动今年…

MySQL数据库事务和存储引擎(贼详细)

目录 一、MySQL 事物 1、事务的概念 2、 事务的ACID特点 &#xff08;1&#xff09;原子性 &#xff08;2&#xff09;一致性 &#xff08;3&#xff09; 隔离性 &#xff08;4&#xff09;事务之间的相互影响 &#xff08;5&#xff09; Mysql及事物隔离级别 &#…

你知道视频配音怎么配出好听的声音吗

小明&#xff1a;最近我在制作一个视频项目&#xff0c;但是我发现视频中的原声不够理想&#xff0c;我想给它配上好听的声音。你知道怎么配音才能让声音听起来更好吗&#xff1f; 小李&#xff1a;当然&#xff01;配音是提升视频质量的重要一环。想知道视频配音怎么配出好听…

uniapp实现应用wgt资源热更新

APP更新一般有两种形式 1、整包更新&#xff0c;通过hbuliderx提供的在线云打包就属于整包更新&#xff0c;属于全量更新&#xff0c;缺点就是打包时间长、要重新走市场审核。费时 2、wgt资源包热更新&#xff0c;通过hbuliderx打wgt包 &#xff0c;速度快&#xff0c;能在应用…

iphone测试中除了appium,还有更好用的工具吗

除了Appium&#xff0c;还有一些其他的工具可以用于iPhone测试&#xff0c;下面列举几个&#xff1a; 1. XCUITest XCUITest是苹果官方提供的UI自动化测试框架&#xff0c;可以用于iPhone和iPad应用程序的自动化测试。XCUITest可以模拟用户操作&#xff0c;例如点击、滑动、输…

机器学习实践(1.1)XGBoost分类任务

前言 XGBoost属于Boosting集成学习模型&#xff0c;由华盛顿大学陈天齐博士提出&#xff0c;因在机器学习挑战赛中大放异彩而被业界所熟知。相比越来越流行的深度神经网络&#xff0c;XGBoost能更好的处理表格数据&#xff0c;并具有更强的可解释性&#xff0c;还具有易于调参…

若依微服务 + seata1.5.2版本分布式事务(安装配置nacos+部署)

若依官方使用的1.4.0版本seata&#xff0c;版本较低配置相对更麻烦一些 一、seata服务端下载&#xff0c;下载方式介绍两种入口&#xff0c;如下&#xff1a; 1、找到对应版本&#xff0c;下载 binary 即可。 下载包名为&#xff1a;seata-server-1.5.2.zip 2. github上下载 …

WWDC2023 Metal swift 头显ARKit支持c c++ 开发

1 今年WWDC&#xff0c;我们看见了苹果的空间计算设备&#xff0c;visionOS也支持了c c API. 这有什么好处呢&#xff0c;不是说能够吸引更多c c开发者加入苹果开发者阵营&#xff0c;而是我们过去的很多软件&#xff0c;可以轻松对接到苹果的头显设备&#xff0c;让我们的软件…

2023年协议转让研究报告

第一章 概述 协议转让作为中国企业破产法中的重要程序之一&#xff0c;已经在实践中得到广泛应用。在协议转让过程中&#xff0c;债务人与债权人或其他相关方通过协商达成一致&#xff0c;将特定的资产或权益进行转让&#xff0c;以实现债务清偿或债务人的破产清算。协议转让的…

RRC重建比率高问题分析和优化方法

PART01 1、重建概述 RRC重建&#xff08;RRC connection re-establishment&#xff09;是UE处于RRC_CONNECTED状态&#xff0c;因为一些移动性管理或底层链路故障&#xff0c;导致连接中断&#xff0c;UE发起的空口资源重新建立的过程&#xff0c;以继续空口的RRC连接。重建是…

[CSP-S 2021] 回文

[CSP-S 2021] 回文 题目描述: 给定正整数 n 和整数序列 a1​,a2​,…,a2n​&#xff0c;在这 2n 个数中&#xff0c;1,2,…,n 分别各出现恰好 2 次。现在进行 2n 次操作&#xff0c;目标是创建一个长度同样为 2n 的序列 b1​,b2​,…,b2n​&#xff0c;初始时 b 为空序列&…