NetCore自带DI容器的三种生命周期浅析

NetCore自带的依赖注入框架,分别由三种生命周期:单例Singleton/瞬时Transient/一次请求Scoped.接下来我将针对这三种生命周期简要的进行使用方法的解析,并不深入,希望各位大佬多多指教。

各生命周期的区别

Singleton:单例,整个程序启动后只存在唯一一份,如同单例模式一样
Transient:瞬时,每一个对象都是不一样的实例
前两者,都是从root容器中获取实例
Scoped:一次请求,即一次Http请求,获取的实例都是同一个,且是从子容器中获取,其实现原理也正是因为从子容器中获取的特点,而可以实现一次请求一个实例
下面是实例:
我自己写了三个接口以及他们对象的实现,取名自动对应上面三个生命周期
ITransientService TransientService
IScopedService ScopedService
ISingletonService SingletonService
分别在实现里面定义了一个变量i,通过这个变量i来观察他们的生命周期。

各生命周期接口实现代码截图如下:

netcore 获取容器ip_依赖注入


netcore 获取容器ip_依赖注入_02


netcore 获取容器ip_.net_03


注入各生命周期接口以及实现截图如下:

netcore 获取容器ip_netcore 获取容器ip_04


netcore 获取容器ip_依赖注入_05


运行结果截图如下:

netcore 获取容器ip_生命周期_06

从运行的结果中可以看出,Transient对象每一个都是不同的,Scoped对象在一次请求中,无论获取多少个,都是同一个,Singleton对象整个程序中只有唯一一份。

各生命周期在实际开发中都用在什么场景

Singleton:单例,主要用在例如数据库,Options,IHostService(netcore自带后台服务跟随程序启动而启动)的注入这些,只需要一次实例化就永久保持,不需要每一次都去获取新的实例
Transient:这个就可以应用在比如领域驱动设计中的应用层Services中
Scoped:这个可以用在数据库的上下文中,事务处理等

同一接口实现或同一对象注入到不同的生命周期中会出现什么情况

这里说的是:如果我把ITransientService这个接口及其实现分别注入到三种生命周期中,这个时候会表现出什么情况呢?

netcore 获取容器ip_1024程序员节_07


netcore 获取容器ip_依赖注入_08


netcore 获取容器ip_依赖注入_09


通过程序的运行结果可以看到,如果把一个对象分别注入到不同的生命周期中,根据注入的先后顺序,后面的会覆盖前面的注入

生命周期嵌套注入会是什么情况

这里说的是:如果我在Singleton中注入Transient模式的对象和Scoped对象会出现什么情况呢?

在Sington中注入Transient对象

netcore 获取容器ip_1024程序员节_10


从运行结果中可以看出来:外部的TransientService依然是瞬时模式,每一个都是不一样的,但是在Singleton内部注入的TransientService,每一个也是不同的实例,但是一但运行后,每一个将保持单例模式不会重新去获取新的实例,通过每一次请求运行结果可以看出来

在Singleton中注入Scoped对象

直接通过构造方法注入报错:

netcore 获取容器ip_netcore 获取容器ip_11


其实也很好解释,因为Singleton是从root容器中获取,而Scoped是从子容器来的,所以找不到注入不了很正常,那么如何注入呢?

方法如下:

第一种方法是在Program文件中增加不验证Scoped的代码:

.UseDefaultServiceProvider(options =>
                {
                    //注册为scope的通过serviceprovider获取报错 验证scope设置为false
                    //解决 Cannot resolve scoped service '' from root provider.
                    options.ValidateScopes = false;
                })

netcore 获取容器ip_生命周期_12


注入之后运行结果如下:

netcore 获取容器ip_生命周期_13


从运行结果中可以看出Scoped都是一个,并且保持单例模式

第二种方法:

通过注入IServiceProvider,创造一个Scope中,Scope中获取一个实例


通过注入IServiceScopeFactory工厂获取一个实例

netcore 获取容器ip_生命周期_14


通过Scoped每次获取的都是不同的,这时候在Singleton中注入Scoped实例,它维持自己所属的范围

如果在Singleton中注入Scoped,然后Scoped中注入Transient会出现什么情况

netcore 获取容器ip_1024程序员节_15


从运行结果可以简单的分析出:

Scoped保持那种生命周期状态,它里面注入的瞬时就保持那种状态

如果在Transient中注入Scoped或Singleton呢

以下使用这几个类来完成解答

ITranService TranService 瞬时接口和实现

IScService ScService 一次请求接口和实现

IOnlyOneService OnlyOneService 单例接口和实现

里面的内容与上面接口和类的一致。

netcore 获取容器ip_生命周期_16


从运行结果可以看出,各自保持自己的生命周期

如果在Scoped中注入Singleton呢

netcore 获取容器ip_依赖注入_17


从运行结果可以看出,还是单例

NetCore自带容器的不足点

1.不支持单接口多实现
2.不支持循环依赖注入
3.不支持属性/字段注入,只能构造函数注入,但是属性字段注入可以使用反射的方式自己实现(这里可以不依赖Autofac)
这些不足都可以使用其他容器替换,由于NetCore框架良好的拓展能力,我们可以使用Autofac这个第三方依赖注入容器实现这些不足

实例代码可以在如下地址获取:
https://github.com/SakuraMayan/LifetimeAnalysis

以上仅是个人在使用中NetCore自带生命周期产生的疑惑,进行编码测试后得出的一些结果,仅供各位大佬查看,若有不妥之处,敬请指出。本文只是浅薄的从使用层面来理解并未深入NetCore依赖注入容器底层,个人水平和时间有限。