.NET Core具有一个承载(Hosting)系统,承载需要在后台长时间运行的服务,一个ASP.NET Core应用仅仅是该系统承载的一种服务而已。承载系统总是采用依赖注入的方式来消费它在服务承载过程所需的服务。对于承载系统来说,原始的服务注册总是体现为一个IServiceCollection集合,最终的依赖注入容器则体现为一个IServiceProvider对象,如果要将第三方依赖注入框架整合进来,就需要利用它们解决从IServiceCollection集合到IServiceProvider对象之间的适配问题。

一、IServiceCollection =>ContainerBuilder=>IServiceProvider

具体来说,我们可以在IServiceCollection集合和IServiceProvider对象之间设置一个针对某个第三方依赖注入框架的ContainerBuilder对象。我们先利用包含原始服务注册的IServiceCollection集合来创建一个ContainerBuilder对象,再利用该对象来构建作为依赖注入容器的IServiceProvider对象。

二、 IServiceProviderFactory<TContainerBuilder>

如上图所示的两种转换是利用一个IServiceProviderFactory<TContainerBuilder>对象完成的。如下面的代码片段所示,IServiceProviderFactory<TContainerBuilder>接口定义了两个方法,其中CreateBuilder方法利用指定的IServiceCollection集合创建出对应的ContainerBuilder对象,而CreateServiceProvider方法则进一步利用这个ContainerBuilder对象创建出作为依赖注入容器的IServiceProvider对象。

public interface IServiceProviderFactory<TContainerBuilder>
{
TContainerBuilder CreateBuilder(IServiceCollection services);
IServiceProvider CreateServiceProvider(TContainerBuilder containerBuilder);
}

.NET Core的承载系统总是利用注册的IServiceProviderFactory<TContainerBuilder>服务来创建最终作为依赖注入容器的IServiceProvider对象。承载系统默认注册的是如下这个DefaultServiceProviderFactory类型。如下面的代码片段所示,DefaultServiceProviderFactory对象会直接调用指定IServiceCollection集合的BuildServiceProvider方法创建出对应的IServiceProvider对象。

public class DefaultServiceProviderFactory :  IServiceProviderFactory<IServiceCollection>
{
public DefaultServiceProviderFactory() : this(ServiceProviderOptions.Default){}
public DefaultServiceProviderFactory(ServiceProviderOptions options) =>_options = options; public IServiceCollection CreateBuilder(IServiceCollection services) => services; public IServiceProvider CreateServiceProvider( IServiceCollection containerBuilder) => containerBuilder.BuildServiceProvider(_options);
}

三、整合第三方依赖注入框架

为了让读者朋友对利用注册的IServiceProviderFactory<TContainerBuilder>服务整合第三方依赖注入框架具有更加深刻的理解,我们来演示一个具体的实例。我们在《一个Mini版的依赖注入框架》创建了一个名为Cat的“迷你版”依赖注入框架,接下来我们将提供一个具体IServiceProviderFactory<TContainerBuilder>实现类型完成对它的整合。

我们首先创建一个名为CatBuilder的类型作为对应的ContainerBuilder。由于需要涉及针对服务范围的创建,我们在CatBuilder类中定了如下两个内嵌的私有类型,其中表示服务范围的ServiceScope对象实际上就是对一个IServiceProvider对象的封装,另一个ServiceScopeFactory类型表示创建该对象的工厂,它是对一个Cat对象的封装。

public class CatBuilder
{
private class ServiceScope : IServiceScope
{
public ServiceScope(IServiceProvider serviceProvider) => ServiceProvider = serviceProvider;
public IServiceProvider ServiceProvider { get; }
public void Dispose()=> (ServiceProvider as IDisposable)?.Dispose();
} private class ServiceScopeFactory : IServiceScopeFactory
{
private readonly Cat _cat;
public ServiceScopeFactory(Cat cat) => _cat = cat;
public IServiceScope CreateScope() => new ServiceScope(_cat);
}
}

一个CatBuilder对象是对一个Cat对象的封装,它的BuildServiceProvider方法会直接返回这个Cat对象,并作为最终提供的依赖注入容器。CatBuilder在初始化过程中添加了针对IServiceScopeFactory接口的服务注册,具体注册的是根据作为当前子容器的Cat对象创建的ServiceScopeFactory对象。为了实现程序集范围内的批量服务注册,我们为CatBuilder定义了一个Register方法。

public class CatBuilder
{
private readonly Cat _cat;
public CatBuilder(Cat cat)
{
_cat = cat;
_cat.Register<IServiceScopeFactory>( c => new ServiceScopeFactory(c.CreateChild()), Lifetime.Transient);
}
public IServiceProvider BuildServiceProvider() => _cat;
public CatBuilder Register(Assembly assembly)
{
_cat.Register(assembly);
return this;
}
...
}

如下所示的CatServiceProviderFactory类型实现了IServiceProviderFactory<CatBuilder>接口。在实现的CreateBuilder方法中,我们创建了一个Cat对象,并将指定IServiceCollection集合包含中的服务注册(ServiceDescriptor对象)转换成兼容Cat的服务注册(ServiceRegistry对象)并应用到创建的Cat对象上。我们最终利用这个Cat对象创建出返回的CatBuilder对象。实现的另一个方法CreateServiceProvider返回的是调用CatBuilder对象的CreateServiceProvider方法得到的IServiceProvider对象。

public class CatServiceProviderFactory : IServiceProviderFactory<CatBuilder>
{
public CatBuilder CreateBuilder(IServiceCollection services)
{
var cat = new Cat();
foreach (var service in services)
{
if (service.ImplementationFactory != null)
{
cat.Register(service.ServiceType, provider ) => service.ImplementationFactory(provider), service.Lifetime.AsCatLifetime());
}
else if (service.ImplementationInstance != null)
{
cat.Register(service.ServiceType, service.ImplementationInstance);
}
else
{
cat.Register(service.ServiceType, service.ImplementationType, service.Lifetime.AsCatLifetime());
}
}
return new CatBuilder(cat);
}
public IServiceProvider CreateServiceProvider(CatBuilder containerBuilder) => containerBuilder.BuildServiceProvider();
}

Cat具有.NET Core依赖注入框架一致的服务生命周期表达方式,所以我们在将服务注册从ServiceDescriptor类型转化成ServiceRegistry类型时,可以实现直接完成两种生命周期模式的转换,具体的转换实现在如下这个AsCatLifetime扩展方法中。

internal static class Extensions
{
public static Lifetime AsCatLifetime(this ServiceLifetime lifetime)
{
return lifetime switch
{
ServiceLifetime.Scoped => Lifetime.Self,
ServiceLifetime.Singleton => Lifetime.Root,
_ => Lifetime.Transient,
};
}
}

接下来我们演示如何利用CatServiceProviderFactory来创建作为依赖注入容器的IServiceProvider对象。我们定义了如下的接口和对应的实现类型,其中Foo、Bar、Baz和Qux类型分别实现了对应的接口IFoo、IBar、IBaz和IQux,其中Qux类型上标注了一个MapToAttribute特性注册了与对应接口IQux之间的映射。为了反映Cat对服务实例生命周期的控制,我们让它们派生于同一个基类Base。Base实现了IDisposable接口,我们在其构造函数和实现的Dispose方法中输出相应的文本以确定对应的实例何时被创建和释放。

public interface IFoo {}
public interface IBar {}
public interface IBaz {}
public interface IQux {}
public interface IFoobar<T1, T2> {}
public class Base : IDisposable
{
public Base() => Console.WriteLine($"Instance of {GetType().Name} is created.");
public void Dispose() => Console.WriteLine($"Instance of {GetType().Name} is disposed.");
} public class Foo : Base, IFoo{ }
public class Bar : Base, IBar{ }
public class Baz : Base, IBaz{ }
[MapTo(typeof(IQux), Lifetime.Root)]
public class Qux : Base, IQux { }
public class Foobar<T1, T2>: IFoobar<T1,T2>
{
public IFoo Foo { get; }
public IBar Bar { get; }
public Foobar(IFoo foo, IBar bar)
{
Foo = foo;
Bar = bar;
}
}

在如下所示的演示程序中,我们创建了一个ServiceCollection集合,并采用三种不同的生命周期模式分别添加了针对IFoo、IBar和IBaz接口的服务注册。我们接下来根据这个ServiceCollection集合创建了一个CatServiceProviderFactory对象,并调用其CreateBuilder方法创建出对应的CatBuilder对象。我们随后调用了CatBuilder对象的Register方法完成了针对当前入口程序集的批量服务注册,其目的在于添加针对IQux/Qux的服务注册。

class Program
{
static void Main()
{
var services = new ServiceCollection()
.AddTransient<IFoo, Foo>()
.AddScoped<IBar>(_ => new Bar())
.AddSingleton<IBaz>(new Baz()); var factory = new CatServiceProviderFactory();
var builder = factory.CreateBuilder(services)
.Register(Assembly.GetEntryAssembly());
var container = factory.CreateServiceProvider(builder); GetServices();
GetServices();
Console.WriteLine("\nRoot container is disposed.");
(container as IDisposable)?.Dispose(); void GetServices()
{
using (var scope = container.CreateScope())
{
Console.WriteLine("\nService scope is created.");
var child = scope.ServiceProvider; child.GetService<IFoo>();
child.GetService<IBar>();
child.GetService<IBaz>();
child.GetService<IQux>(); child.GetService<IFoo>();
child.GetService<IBar>();
child.GetService<IBaz>();
child.GetService<IQux>();
Console.WriteLine("\nService scope is disposed.");
}
}
}
}

在调用CatServiceProviderFactory对象的CreateServiceProvider方法创建出作为依赖注入容器的IServiceProvider对象之后,我们先后两次调用了本地方法GetServices方法。GetServices方法会利用这个IServiceProvider对象创建一个服务范围,并利用此服务范围内的IServiceProvider提供两组服务实例。通过CatServiceProviderFactory创建的IServiceProvider对象在最终通过调用其Dispose方法进行释放。该程序运行之后会在控制台上输出如图4-16所示的结果,输出结果体现的服务生命周期与演示程序体现的是完全一致的。

[ASP.NET Core 3框架揭秘] 依赖注入[1]:控制反转
[ASP.NET Core 3框架揭秘] 依赖注入[2]:IoC模式
[ASP.NET Core 3框架揭秘] 依赖注入[3]:依赖注入模式
[ASP.NET Core 3框架揭秘] 依赖注入[4]:一个迷你版DI框架
[ASP.NET Core 3框架揭秘] 依赖注入[5]:利用容器提供服务
[ASP.NET Core 3框架揭秘] 依赖注入[6]:服务注册
[ASP.NET Core 3框架揭秘] 依赖注入[7]:服务消费
[ASP.NET Core 3框架揭秘] 依赖注入[8]:服务实例的生命周期
[ASP.NET Core 3框架揭秘] 依赖注入[9]:实现概述
[ASP.NET Core 3框架揭秘] 依赖注入[10]:与第三方依赖注入框架的适配

[ASP.NET Core 3框架揭秘] 依赖注入[10]:与第三方依赖注入框架的适配的更多相关文章

  1. ASP.NET Core Web 应用程序系列(一)- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入(MVC当中应用)

    在正式进入主题之前我们来看下几个概念: 一.依赖倒置 依赖倒置是编程五大原则之一,即: 1.上层模块不应该依赖于下层模块,它们共同依赖于一个抽象. 2.抽象不能依赖于具体,具体依赖于抽象. 其中上层就 ...

  2. Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  3. Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员、后台管理员同时登录

    1.登录的实现 登录功能实现起来有哪些常用的方式,大家首先想到的肯定是cookie或session或cookie+session,当然还有其他模式,今天主要探讨一下在Asp.net core 2.0下 ...

  4. ASP.NET Core 6框架揭秘实例演示[04]:自定义依赖注入框架

    ASP.NET Core框架建立在一个依赖注入框架之上,已注入的方式消费服务已经成为了ASP.NET Core基本的编程模式.为了使读者能够更好地理解原生的注入框架框架,我按照类似的设计创建了一个简易 ...

  5. ASP.NET Core 6框架揭秘实例演示[10]:Options基本编程模式

    依赖注入使我们可以将依赖的功能定义成服务,最终以一种松耦合的形式注入消费该功能的组件或者服务中.除了可以采用依赖注入的形式消费承载某种功能的服务,还可以采用相同的方式消费承载配置数据的Options对 ...

  6. ASP.NET CORE MVC 2.0 如何在Filter中使用依赖注入来读取AppSettings,及.NET Core控制台项目中读取AppSettings

    问: ASP.NET CORE MVC 如何在Filter中使用依赖注入来读取AppSettings 答: Dependency injection is possible in filters as ...

  7. ASP.NET Core:使用Dapper和SwaggerUI来丰富你的系统框架

    一.概述 1.用VS2017创建如下图的几个.NET Standard类库,默认版本为1.4,你可以通过项目属性进行修改,最高支持到1.6,大概五月份左右会更新至2.0,API会翻倍,很期待! 排名分 ...

  8. 菜鸟入门【ASP.NET Core】6:配置的热更新、配置的框架设计

    配置的热更新 什么是热更新:这个词听着有点熟悉,但到底是什么呢? 一般来说:创建的项目都无法做到热更新:即项目无需重启,修改配置文件后读取到的信息就是修改配置之后的 我们只需要吧项目中用到的IOpti ...

  9. 从ASP.Net Core Web Api模板中移除MVC Razor依赖项

    前言 :本篇文章,我将会介绍如何在不包括MVC / Razor功能和包的情况下,添加最少的依赖项到ASP.NET Core Web API项目中. 一.MVC   VS WebApi (1)在ASP. ...

  10. 【ASP.NET Core】准备工作:在 Windows 10 上配置 Linux 子系统

    ASP.NET Core 其实比传统的 ASP.NET 要简单很多,而且也灵活很多,并且可以跨平台独立运行. 在 Windows 平台上,我们只要在安装 Visual Studio 的时候选择跨平台的 ...

随机推荐

  1. 小程序取消IOS虚拟支付解决方案

    前因 本来我们的小程序用的好好的,结果突然有一天,微信就把小程序的ios端的虚拟支付给关了...坑爹啊!搞的安卓端的可以支付,ios的支付不了.于是就在网上找解决办法. 一说通过app跳转支付,总不能 ...

  2. 【2018寒假集训 Day2】【动态规划】维修栅栏

    维修栅栏 问题描述: 小z最近当上了农场主!不过,还没有来得及庆祝,一件棘手的问题就摆在了小z的面前.农场的栅栏,由于年久失修,出现了多处破损.栅栏是由n块木板组成的,每块木板可能已经损坏也可能没有损 ...

  3. Session.run() & Tensor.eval()

    如果有一个Tensor t,在使用t.eval()时,等价于: tf.get_defaut_session().run(t) t = tf.constant(42.0) sess = tf.Sessi ...

  4. vscode + platformIO开发stm32f4

    我的电脑环境 win10 vscode 1.36.1 vscode安装插件 安装完这个插件后会提示你安装 platformIOCore,按照提示安装即可.安装过程可能比较缓慢, 可能需要翻墙. 新建项 ...

  5. 在linux和windows中使用selenium

     在linux和windows中使用selenium 一. selenium(浏览的人你们多大呀?是AI?) selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法 ...

  6. SpringMVC 请求参数绑定

    什么是请求参数绑定 请求参数格式 默认是key/value格式,比如:http:xxxx?id=1&type=2 请求参数值的数据类型 都是字符串类型的各种值 请求参数值要绑定的目标类型 Co ...

  7. Java泛型之上、下界通配符的理解(适合初学)

    泛型的由来 为什么需要泛型   Java的数据类型一般都是在定义时就需要确定,这种强制的好处就是类型安全,不会出现像弄一个ClassCastException的数据给jvm,数据安全那么执行的clas ...

  8. webpack4.0各个击破(6)—— Loader篇【华为云技术分享】

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...

  9. Modelarts与无感识别技术生态总结(浅出版)

    [摘要] Modelarts技术及相关产业已成为未来AI与大数据重点发展行业模式之一,为了促进人工智能领域科学技术快速发展,modelarts现状及生态前景成为研究热点.笔者首先总结modelarts ...

  10. 使用Spring Data JPA的Specification构建数据库查询

    Spring Data JPA最为优秀的特性就是可以通过自定义方法名称生成查询来轻松创建查询SQL.Spring Data JPA提供了一个Repository编程模型,最简单的方式就是通过扩展Jpa ...