ASP.NET Core应用中与第三方IoC/DI框架的整合
更新时间:2017年04月11日 11:39:43 作者:蒋金楠
ASP.NET Core应用中,针对第三方DI框架的整合可以通过在定义Startup类型的ConfigureServices方法返回一个ServiceProvider来实现。但是并不是那么容易的,下面通过实例给大家分享一下
一、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);
}
…
}
…
}
以上分享,希望能对需要解决这样问题的朋友有所帮助!
您可能感兴趣的文章:ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统之前端页面框架构建源码分享asp.net CommunityServer中的wwwStatus.Net Core简单使用Mvc内置的Ioc(续).Net Core简单使用Mvc内置的IocSpring.Net控制反转IoC入门使用MVC使用Spring.Net应用IOC(依赖倒置)学习笔记3.net MVC使用IPrincipal进行Form登录即权限验证(3).net MVC使用Session验证用户登录(4).NET Unity IOC框架使用实例详解
ASP.NET
Core
ioc
框架
相关文章
防SQL注入 生成参数化的通用分页查询语句前些时间看了玉开兄的“如此高效通用的分页存储过程是带有sql注入漏洞的”这篇文章,才突然想起某个项目也是使用了累似的通用分页存储过程。 2010-07-07
asp.net微信开发(用户分组管理)这篇文章主要介绍了asp.net微信开发中有关用户分组管理的相关内容,需要的朋友可以参考下 2015-11-11
ADO.NET实用经验汇总这篇文章主要介绍了ADO.NET实用经验汇总,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 2019-12-12
Asp.net在ashx文件中处理Session问题解决方法Asp.net在ashx文件中处理Session问题解决方法,需要的朋友可以参考一下 2013-05-05
asp.net下用js实现鼠标移至小图,自动显示相应大图asp.net下用js实现鼠标移至小图,自动显示相应大图… 2007-03-03
asp.net Request.ServerVariables[] 读解asp.net Request.ServerVariables[] 读解,学习.net的朋友可以参考下,方便获取服务器的一些信息。 2011-08-08
ASP.NET在MVC控制器中获取Form表单值的方法这篇文章主要介绍了ASP.NET在MVC控制器中获取Form表单值的方法,涉及asp.net在MVC控制器中基于FormCollection类操作表单的相关技巧,需要的朋友可以参考下 2016-08-08
asp.net中javascript的引用(直接引入和间接引入)Asp.net 中引入Javascript的方法有很多,个人认为可以分为直接引入,和间接引入,下面是两种具体的引用方法,感兴趣的朋友可以参考下哈 2013-06-06
asp.net ajax实现无刷新验证码实现ajax无刷新验证码首先需要两个aspx页面,第一个用来展示,另一个用来后台刷新验证码 2011-10-10
使用Lucene.NET实现站内搜索提到Lucene,想必大家都有所耳闻,已经是数年前就出现的开源技术。很多站点都是利用它搭建自己网站的站内搜索。由于最近也在做数据检索方面的东西,也学习了下Lucene.net的使用。 2015-06-06
最新评论

评论(0)