作用域

  • IServiceScope
  • 作用域主要是由IServiceScope这个接口来承载的

实现IDisposable接口类型的释放

  • 对于实现了IDisposable类的实例的对象,我们容器会负责对其生命周期的管理,当我们使用完毕以后,它会去释放这些对象。
  • DI(容器)只负责由其创建的对象的实例
    • 如果这个对象是由我们创建并放到容器里的,容器是不负责释放这个对象的
  • DI在容器或子容器释放时,释放由其创建的对象实例
  • 也就是说,容器的生命周期与其创建的对象的生命周期是有对应关系的。


建议

  • 避免在根容器获取实现了IDisposable接口的瞬时服务
  • 避免手动创建实现了IDisposable对象,应该使用容器来管理其生命周期

例子

首先我们新建Web程序👉选择API模板👉新建Service文件夹👉定义一个Service
这里我们创建一个服务,实现了IOrderServiceIDisposable

  1. namespace StarupDemo.Services
  2. {
  3. public interface IOrderService
  4. {
  5. }
  6. public class DisposableOrderService : IOrderService, IDisposable
  7. {
  8. public void Dispose()
  9. {
  10. Console.WriteLine($"DisposableOrderService.Disposed : {GetHashCode()}");
  11. }
  12. }
  13. }

然后,我们暂时注册一个瞬时的服务。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddTransient<IOrderService, DisposableOrderService>();
  4. services.AddControllers();
  5. }

Controler中添加方法,通过FromServices注册了两次

  1. public void Get([FromServices]IOrderService service1,[FromServices]IOrderService service2)
  2. {
  3. }

我们这里不需要写任何内容,因为整个生命周期是由容器去管理的。
启动程序。执行完发现,释放时两个对象HashCode是不同的,瞬时服务在每一次获取的时候,都会获取一个新的对象。
image.png
接着我们在Get方法中添加一行输出,执行之后发现
image.png
这意味这我们的对象是在整个请求结束之后,才会触发释放动作。

接下来将生命周期修改为Scopeservice.AddScoped<IOrderService, DisposableOrderService>();
为了方便演示,在Get方法内添加如下代码:

  1. public void Get([FromServices]IOrderService service1,[FromServices]IOrderService service2)
  2. {
  3. // HttpContext.RequestServices是指当前请求的一个容器,也就是说是我们当前请求的一个根容器,
  4. // 是应用程序根容器的一个子容器。每个请求会创建一个容器。
  5. // 在这个我们在这个子容器下面,我们再创建一个子容器来获取我们的服务
  6. using (IServiceScope scope = HttpContext.RequestServices.CreateScope())
  7. {
  8. var service = scope.ServiceProvider.GetService<IOrderService>();
  9. // 再次创建也是获取的同一个对象,Scope方式注册。
  10. }
  11. Console.WriteLine("接口请求处理结束!");
  12. }

运行结果如下:
image.png
每次请求我们会获得两个释放,意味着我们每创建一个子容器,每个作用域内我们可以是单例的,只能获取相同的对象。
普通单例模式,不测试了,结论是不会释放的,因为是单例模式。

我们将服务调整为单例模式,并且是我们自己创建的。
var service = new DisposableOrderService();
services.AddSingleton<IOrderService>(service);
image.png
容器是不会帮助我们去管理对象的生命周期的,怎么去识别这个区别。

Get方法做一个小的调整

  1. // IHostApplicationLifetime 这个接口是用来管理我们整个应用程序的生命周期
  2. public void Get([FromServices]IOrderService service1,
  3. [FromServices]IOrderService service2,
  4. [FromServices]IHostApplicationLifetime hostApplicationLifetime,
  5. [FromQuery]bool stop = false)
  6. {
  7. //using (IServiceScope scope = HttpContext.RequestServices.CreateScope())
  8. //{
  9. // var service = scope.ServiceProvider.GetService<IOrderService>();
  10. //}
  11. //Console.WriteLine("接口请求处理结束!");
  12. if (stop)
  13. {
  14. // StopApplication方法,把整个应用程序关掉
  15. hostApplicationLifetime.StopApplication();
  16. }
  17. }

执行结果:
image.png
将注入方式修改为容器管理的话。services.AddSingleton<IOrderService, DisposableOrderService>();
image.png

接着我们将注册方式修改为瞬时的然后又在根容器内去获取我们这个对象。这意味着我们会在根容器去持续的创建我们的service,但是由于根容器只会在应用程序整个退出时回收,也就意味着我们的这些对象会一直积累在应用程序内。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddTransient<IOrderService, DisposableOrderService>();
  4. services.AddControllers();
  5. }
  6. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  7. {
  8. var s = app.ApplicationServices.GetService<IOrderService>();
  9. var s2 = app.ApplicationServices.GetService<IOrderService>();
  10. if (env.IsDevelopment())
  11. {
  12. app.UseDeveloperExceptionPage();
  13. }
  14. app.UseHttpsRedirection();
  15. app.UseRouting();
  16. app.UseStaticFiles();
  17. app.UseEndpoints(endpoints =>
  18. {
  19. endpoints.MapControllers();
  20. });
  21. }
  22. // IHostApplicationLifetime 这个接口是用来管理我们整个应用程序的生命周期
  23. public void Get(
  24. [FromServices]IHostApplicationLifetime hostApplicationLifetime,
  25. [FromQuery]bool stop = false)
  26. {
  27. //using (IServiceScope scope = HttpContext.RequestServices.CreateScope())
  28. //{
  29. // var service = scope.ServiceProvider.GetService<IOrderService>();
  30. //}
  31. //Console.WriteLine("接口请求处理结束!");
  32. if (stop)
  33. {
  34. // StopApplication方法,把整个应用程序关掉
  35. hostApplicationLifetime.StopApplication();
  36. }
  37. }

image.png