06 | 作用域与对象释放行为:你知道IDisposable对象释放的时机和坑吗?
作用域
- IServiceScope
- 作用域主要是由IServiceScope这个接口来承载的
实现IDisposable接口类型的释放
- 对于实现了IDisposable类的实例的对象,我们容器会负责对其生命周期的管理,当我们使用完毕以后,它会去释放这些对象。
- DI(容器)只负责由其创建的对象的实例
- 如果这个对象是由我们创建并放到容器里的,容器是不负责释放这个对象的
- DI在容器或子容器释放时,释放由其创建的对象实例
- 也就是说,容器的生命周期与其创建的对象的生命周期是有对应关系的。
建议
- 避免在根容器获取实现了IDisposable接口的瞬时服务
- 避免手动创建实现了IDisposable对象,应该使用容器来管理其生命周期
例子
首先我们新建Web程序👉选择API模板👉新建Service文件夹👉定义一个Service
这里我们创建一个服务,实现了IOrderService
和IDisposable
namespace StarupDemo.Services
{
public interface IOrderService
{
}
public class DisposableOrderService : IOrderService, IDisposable
{
public void Dispose()
{
Console.WriteLine($"DisposableOrderService.Disposed : {GetHashCode()}");
}
}
}
然后,我们暂时注册一个瞬时的服务。
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IOrderService, DisposableOrderService>();
services.AddControllers();
}
Controler
中添加方法,通过FromServices
注册了两次
public void Get([FromServices]IOrderService service1,[FromServices]IOrderService service2)
{
}
我们这里不需要写任何内容,因为整个生命周期是由容器去管理的。
启动程序。执行完发现,释放时两个对象HashCode
是不同的,瞬时服务在每一次获取的时候,都会获取一个新的对象。
接着我们在Get
方法中添加一行输出,执行之后发现
这意味这我们的对象是在整个请求结束之后,才会触发释放动作。
接下来将生命周期修改为Scope services.``AddScoped``<IOrderService, DisposableOrderService>();
为了方便演示,在Get方法内添加如下代码:
public void Get([FromServices]IOrderService service1,[FromServices]IOrderService service2)
{
// HttpContext.RequestServices是指当前请求的一个容器,也就是说是我们当前请求的一个根容器,
// 是应用程序根容器的一个子容器。每个请求会创建一个容器。
// 在这个我们在这个子容器下面,我们再创建一个子容器来获取我们的服务
using (IServiceScope scope = HttpContext.RequestServices.CreateScope())
{
var service = scope.ServiceProvider.GetService<IOrderService>();
// 再次创建也是获取的同一个对象,Scope方式注册。
}
Console.WriteLine("接口请求处理结束!");
}
运行结果如下:
每次请求我们会获得两个释放,意味着我们每创建一个子容器,每个作用域内我们可以是单例的,只能获取相同的对象。
普通单例模式,不测试了,结论是不会释放的,因为是单例模式。
我们将服务调整为单例模式,并且是我们自己创建的。
var service = new DisposableOrderService();
services.AddSingleton<IOrderService>(service);
容器是不会帮助我们去管理对象的生命周期的,****怎么去识别这个区别。
将Get
方法做一个小的调整
// IHostApplicationLifetime 这个接口是用来管理我们整个应用程序的生命周期
public void Get([FromServices]IOrderService service1,
[FromServices]IOrderService service2,
[FromServices]IHostApplicationLifetime hostApplicationLifetime,
[FromQuery]bool stop = false)
{
//using (IServiceScope scope = HttpContext.RequestServices.CreateScope())
//{
// var service = scope.ServiceProvider.GetService<IOrderService>();
//}
//Console.WriteLine("接口请求处理结束!");
if (stop)
{
// StopApplication方法,把整个应用程序关掉
hostApplicationLifetime.StopApplication();
}
}
执行结果:
将注入方式修改为容器管理的话。services.AddSingleton<IOrderService, DisposableOrderService>();
接着我们将注册方式修改为瞬时的,然后又在根容器内去获取我们这个对象。这意味着我们会在根容器去持续的创建我们的service
,但是由于根容器只会在应用程序整个退出时回收,也就意味着我们的这些对象会一直积累在应用程序内。
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IOrderService, DisposableOrderService>();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var s = app.ApplicationServices.GetService<IOrderService>();
var s2 = app.ApplicationServices.GetService<IOrderService>();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseStaticFiles();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
// IHostApplicationLifetime 这个接口是用来管理我们整个应用程序的生命周期
public void Get(
[FromServices]IHostApplicationLifetime hostApplicationLifetime,
[FromQuery]bool stop = false)
{
//using (IServiceScope scope = HttpContext.RequestServices.CreateScope())
//{
// var service = scope.ServiceProvider.GetService<IOrderService>();
//}
//Console.WriteLine("接口请求处理结束!");
if (stop)
{
// StopApplication方法,把整个应用程序关掉
hostApplicationLifetime.StopApplication();
}
}
本文作者:hiwwwk
本文链接:https://www.cnblogs.com/wwwk/p/15873341.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步