实现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... |
得出结论:由于根容器只会在应用程序整个退出时回收,这就意味着即使这是个瞬时服务,但是应用程序不退出,这些对象会一直积累在应用程序内不得释放