一、 服务的声明周期概述
- 服务的本质就是一个或一组被DI容器管理的对象,每一次应用程序都要从DI容器中请求这个对象;
- 服务的生命周期其实就是研究这个对象在容器中被请求时返回的究竟是什么样的对象。所有的可能性一共三种:
(1) 无论在哪里请求,容器提供的都是一个全新的对象
(2)无论在哪里请求,容器提供的都是不变的同一个对象
(3)只有在同一个区域请求,容器才会返回同一个对象,不同的区域返回的是不同的对象
以上三种可能性,分别对应服务的瞬时(Transient)、单例(Singleton)以及范围(Scope)生命周期。 - 服务生命周期在注册服务的时候就由服务提供方(也就是注册者)确定。通过调用Service.AddXXX<TService>()方法,其中XXX是服务的生命周期,如Services.AddTransient<TService>()、Services.AddScope<TServices>()等。
二、瞬时生命周期
1.服务的瞬时生命周期:每一次获取服务对象,拿到的都是一个新的对象:
输出:false
证明两次拿到的瞬时服务对象不是同一个对象
三、Scope(范围)生命周期
- 范围生命周期:在某一范围内,获取到的对象是同一个服务对象。该范围由托管的资源释放时间决定。类似于设计模式中的单例设计模式,而这个范围则是由服务资源的托管范围所决定的。
- 创建服务资源托管范围:使用IServiceProvider.CreateScope()方法可以创建一个Scope
上面的代码在创建一个的Scope范围内拉取了两次服务。第一个拉取服务实例,获取服务对象testService1,并将其Name属性设置为“张三”。随后在该范围内再次拉取服务实例,并将这个实例的Name设置为“Lily”。由于在同一个Scope中,所有testService1与testService2实质上是同一个对象。在修改了testService2对象的Name属性时,相应的testService1的Name属性也被相应的修改掉了。所有最后输出的两个Name属性的值均为“Lily”,且两个对象是完全一致的(object.ReferenceEquals()方法返回true)
2.注意事项
(1)若一个类实现了IDisposable接口,则离开作用域后容器会自动调用对象的Dispose方法(类似于using自动管理范围内的对象)
(2)不要在长生命周期的对象中引用比它短的生命周期的对象。在ASP.NET Core中,默认会抛异常
(3)生命周期的选择:若类无状态(没有属性,没有成员变量,例如普通的Services),建议为Singleton;如果类有状态,且有Scope控制,建议为Scoped。因为通常这种Scope控制下的代码都是运行在同一个线程中的。
四、注册服务时的生命周期控制
1.在向容器中注册服务时,可以通过调用不同的注册方法控制注册服务的生命周期。
(1)AddTransint:注册的服务为瞬时生命周期
(2)AddScoped:注册的服务为范围生命周期
(3)AddSingleton:注册的服务为单例模式