当前位置:首页 > 日记 > 正文

ASP.NET Core应用中与第三方IoC/DI框架的整合

ASP.NET Core应用中与第三方IoC/DI框架的整合

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

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

 public class MyServiceProvider : IServiceProvider, IDisposable { private IServiceProvider _innerServiceProvider; private Dictionary<Type, Type> _services; private List<IDisposable> _disposables;  public MyServiceProvider(IServiceProvider innerServiceProvider) { _innerServiceProvider = innerServiceProvider; this._services = new Dictionary<Type, Type>(); _disposables = new List<IDisposable>(); }   public MyServiceProvider Register<TFrom, TTo>() where TTo: TFrom, new() { _services[typeof(TFrom)] = typeof(TTo); return this; }  public object GetService(Type serviceType) { Type implementation; if (_services.TryGetValue(serviceType, out implementation)) { object service = Activator.CreateInstance(implementation); IDisposable disposbale = service as IDisposable; if (null != disposbale) {  _disposables.Add(disposbale); } return service; } return _innerServiceProvider.GetService(serviceType); }  public void Dispose() { (_innerServiceProvider as IDisposable)?.Dispose(); foreach (var it in _disposables) { it.Dispose(); } _disposables.Clear(); } }

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

 public class Program { public static void Main(string[] args) { new WebHostBuilder() .UseKestrel() .UseStartup<Startup>() .Build() .Run(); } }  public class Startup { public IServiceProvider ConfigureServices(IServiceCollection services) { return new MyServiceProvider(services.BuildServiceProvider()) .Register<IFoobar, Foobar>(); }  public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage() .Run(async context => await context.Response.WriteAsync(context.RequestServices.GetRequiredService<IFoobar>().GetType().Name)); } } public interface IFoobar { } 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。

 internal class ServiceScope : IServiceScope { private MyServiceProvider _serviceProvider;  public ServiceScope(IServiceScope innserServiceScope, Dictionary<Type, Type> services) { _serviceProvider = new MyServiceProvider(innserServiceScope.ServiceProvider, services); } public IServiceProvider ServiceProvider { get { return _serviceProvider; } }  public void Dispose() { _serviceProvider.Dispose(); } }  internal class ServiceScopeFactory : IServiceScopeFactory { private IServiceScopeFactory _innerServiceFactory; private Dictionary<Type, Type> _services;  public ServiceScopeFactory(IServiceScopeFactory innerServiceFactory, Dictionary<Type, Type> services) { _innerServiceFactory = innerServiceFactory; _services = services; } public IServiceScope CreateScope() { return new ServiceScope(_innerServiceFactory.CreateScope(), _services); } }

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

 public class MyServiceProvider : IServiceProvider, IDisposable{ public MyServiceProvider(IServiceProvider innerServiceProvider, Dictionary<Type, Type> services) { _innerServiceProvider = innerServiceProvider; _services = services; _disposables = new List<IDisposable>(); }  public object GetService(Type serviceType) { if (serviceType == typeof(IServiceScopeFactory)) { IServiceScopeFactory innerServiceScopeFactory = _innerServiceProvider.GetRequiredService<IServiceScopeFactory>(); return new ServiceScopeFactory(innerServiceScopeFactory, _services); } ...  } ... }

以上分享,希望能对需要解决这样问题的朋友有所帮助!

相关文章

PS怎么利用两张照片合成佛光普照效

PS怎么利用两张照片合成佛光普照效

普照,照片,佛光,两张,效果,Photoshop软件作为一款比较强大的修图软件,它在修图方面有着无可比拟的优势,这也是它能给我们创新一个神奇世界的优势之处。以下小编&ldquo;林曌乐园&rdquo;便为大家分享一下如何使用PS软件制作&ldquo;佛光普照&rdq…

JS数组操作中的经典算法实例讲解

JS数组操作中的经典算法实例讲解

数组操作,经典算法,实例,电脑软件,JS,冒泡排序<script type="text/javascript">var arr = [3,7,6,2,1,5];定义一个交换使用的中间变量var temp = 0;for(i=0;i<arr.length;i++){ for(j=0;j<arr.length;j++){如果下一个元素小于当前元素 …

ppt2010怎样设置4比3模式PPT设置放

ppt2010怎样设置4比3模式PPT设置放

设置,模式,方法,比例,电脑软件,  ppt2013默认为宽屏我16:9,怎么才能设置成正常的4:3的显示呢?对于新手来说还是有一定难度,怎么办?不用着急,接下来小编举例简单的例子告诉大家ppt2010设置4 3模式的方法。ppt2010设置4比3模式的方法打开ppt20…

jQuery.Sumoselect插件实现下拉复

jQuery.Sumoselect插件实现下拉复

复选框,插件,效果,电脑软件,jQuery,简单介绍jquery.sumoselect是一款跨设备、跨浏览器的jQuery下拉列表框插件。该jQuery下拉列表框插件可以单选,也可以多选。它的样式可以通过CSS文件来自定义。它的最大特点是可以跨设备使用,所有设备上功能…

入门:AI旋转复制法使用方法

入门:AI旋转复制法使用方法

使用方法,入门,电脑软件,AI,前面我们已经介绍了ai软件里面常用的两种复制方法,接下来我们介绍第三种复制方法&mdash;&mdash;旋转复制法。1、新建画布,用矩形工具任意在画布上绘制一个小横条红色矩形2、在软件界面左边的工具栏里面,有个旋转箭…

JS实现手写parseInt的方法示例

JS实现手写parseInt的方法示例

方法,示例,电脑软件,JS,parseInt,前言本文主要给大家介绍了关于JS实现手写parseInt的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。手写parseInt的实现:要求简单一些,把字符串型的数字转化为真正的数字即可,但不能使…

如何通过模仿快速提高PPT制作水平

如何通过模仿快速提高PPT制作水平

快速,电脑软件,PPT,  想快速提高自己的PPT水平保持一颗好奇心就足够了,但如果对PPT的一些基本设计原理都缺乏了解,发现一个PPT的设计之美是很困难的,这时候,最好的方法就是模仿。以下是小编为您带来的关于通过模仿快速提高PPT制作水平,希望对…

js从输入框读取内容,比较两个数字

js从输入框读取内容,比较两个数字

数字,输入框,方法,读取,大小,因为从输入框读取的内容是String类型的,所以直接比较的话,是用asc码来比较的,如果要比较数字的大小应该加上parseFloat,比如:if(parseFloat(amnt1) < parseFloat(amnt2)){}以上这篇js从输入框读取内容,比较两个数字…

ps怎么使用动作是对图形进行多色填

ps怎么使用动作是对图形进行多色填

填充,图形,多色,动作,电脑软件,ps中想要填充图形,该怎么填充不同颜色的图形呢?下面我们就来看看详细的教程。软件名称:Adobe Photoshop 8.0 中文完整绿色破解版软件大小:150.1MB更新时间:2015-11-041、在桌面上双击Photoshop的快捷图标,打开Photo…

在Windows主机上绑定多IP和多域名

在Windows主机上绑定多IP和多域名

主机,绑定,多域名,详解,常用,本文介绍Windows主机绑定多IP和多域名常用办法,多IP和多域名配置往往使用在独立主机和虚拟专用服务器(VPS)上,按照一些SEO大佬的说法,独立IP可以给域名更高的权重,所以很多购买VPS的用户大都还给自己同一主机的域名购…

ps中怎么制作竹简上刻字的效果?

ps中怎么制作竹简上刻字的效果?

竹简,效果,电脑软件,ps,木刻字效果主要使用图层样式制作而成,现在就把制作的过程步骤以及截图分享出来,供大家参考使用,同时也希望你能分享更多更好的优质经验,效果如下文所示。软件名称:Adobe Photoshop 8.0 中文完整绿色破解版软件大小:150.1MB…

xls文件怎么打开 xls文件打开空白

xls文件怎么打开 xls文件打开空白

文件,解决方法,空白,电脑软件,xls,  今天股市里面的一个老师发来一个 .xls 文件,说是什么报名表,让看一下,现在对天朝股市都已经没什么信息了,好吧,还是打开看一下吧,结果在我的笔记本上面双击,打开倒是可以,但是页面空白什么内容都没有,只能再去…