实现IDisposable
接口类型的释放
DI
只负责释放其创建的对象实例DI
在容器或子容器释放时,释放尤其创建的对象实例
建议
- 避免在根容器获取实现了
IDisposable
接口的瞬时服务 - 避免手动创建实现了
IDisposable
对象,应该使用容器来管理其生命周期
代码验证
项目
创建名为DependencyInjectionScopeAndDisposableDemo
的ASP.NET Core
项目,类型为API
创建测试服务
创建测试服务类OrderService.cs
,代码如下:
1 | using System; |
测试Transient
服务的释放时机
在Startup.ConfigureServices
里注册一个瞬时服务
1 | services.AddTransient<IOrderService, DisposableOrderService>(); |
在WeatherForecastController
里的Get
方法通过方法参数的形式获取两个IOrderService
接口对象,代码如下:
1 | [ ] |
保存之后运行项目,可以看到在打印接口请求处理结束之后两个对象都被释放掉,类似以下信息
1 | =====接口请求处理结束==== |
得出的结论是:transient对象会在使用后被释放
测试Scope
服务的释放时机
注释掉上一步注册瞬时服务的代码,重新注册一个scope
服务,这里使用工厂模式,只是熟悉以下工厂模式的写法,没特殊意义
1 | services.AddScoped<IOrderService>(serviceProvider => new DisposableOrderService()); |
这时运行项目,控制台会打印出一个对象被释放,因为scope
服务在当前容器内为单例,下面就这个结论再次测试以下,用using创建一个服务容器出来,看是否能创建出新的服务对象,并且代码运行超过using范围,服务对象是否会被释放。在WeatherForecastController
的Get
方法新增创建容器和获取服务操作,代码如下:
1 | [ ] |
HttpContext.RequestServices.CreateScope()
表示从根容器中创建一个子容器出来,scope.ServiceProvider.GetService<IOrderService>()
表示从创建出来的子容器获取IOrderService
的服务实现对象,运行项目可以得到两个对象被释放的信息,类似以下信息:
1 | ========1========== |
得出结论是:scope对象会在对象产生的容器被释放的时候同时一起释放
测试Singleton
服务的释放时机
注释掉上一步注册scope服务的代码,重新注册一个singleton
服务,这里一样使用工厂模式
1 | services.AddSingleton<IOrderService>(serviceProvider => new DisposableOrderService()); |
将WeatherForecastController
的Get
方法修改为以下代码:
1 | [ ] |
这里有获取了另一个服务IHostApplicationLifetime
,这个服务对象控制了整个应用程序的生命周期,可以调用这个服务对象的StopApplication
方法来停止应用程序,即停止整个站点,为了方便测试这里用了isStop
这个参数来控制是否停止应用程序
运行项目,不管怎么刷新/WeatherForecast
链接,都不会有对象被释放的信息打印出来,访问/WeatherForecast?isStop=true
的时候,会看到应用程序被停止,同时打印出对象被释放的信息,类似以下信息:
1 | Application is shutting down... |
得出结论是:scope对象只会在根容器释放的时候才会被释放,即所有单例模式的对象都会被注册在根容器上面
避坑
自己new
服务
开始有个结论:DI
只负责释放其创建的对象实例,这里进行验证以下自己new一个对象然后注入到容器中,看看是否能释放。这里注入了一个singleton
对象,需注释掉上面做测试的代码,代码如下
1 | var myOrderService = new DisposableOrderService(); |
控制器代码不用改,运行项目,访问/WeatherForecast
发现不会有对象被释放,访问/WeatherForecast?isStop=true
时,应用程序被停止,但是也一样没有对象被释放的信息,所以这个对象最终还是没被释放
在跟容器获取Transient
服务
注释掉上面的测试代码,在Startup.ConfigureServices
中注册一个瞬时服务,代码如下:
1 | services.AddTransient<IOrderService, DisposableOrderService>(); |
在Startup.Configure
中从根容器获取瞬时服务对象,代码如下:
1 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
将WeatherForecastController
的Get
方法修改为以下代码:
1 | [ ] |
运行项目,访问/WeatherForecast
接口,发现并不会有对象被释放的信息,在带上isStop=true
的参数的时候,应用程序被停止,这时才有对象被释放的信息,类似信息如下:
1 | Application is shutting down... |
得出结论:由于根容器只会在应用程序整个退出时回收,这就意味着即使这是个瞬时服务,但是应用程序不退出,这些对象会一直积累在应用程序内不得释放