其他分享
首页 > 其他分享> > 作用域和对象释放的时机和坑

作用域和对象释放的时机和坑

作者:互联网

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

实现IDisposable接口类型的释放

解读:
对于实现了IDisposable类的实例的对象,我们容器会负责去对其生命周期的管理,当我们使用完毕以后它会去释放这些对象,但是需要注意的有两点:

建议

解读:
在这里面的话我们有两点是需要注意的:
第一个就是:在根容器,我们最好不要去创建瞬时服务,实现了IDisposable的瞬时服务;
第二个就是:避免手动创建对象 然后塞到容器里面去,我们应该尽可能的使用容器,来去管理我们的对象的创建和释放;

瞬时Transient服务

public interface IOrderService{}
public class DisposableOrderService: IOrderService, IDisposable{
    public void Dispose(){
        Console.WriteLine($"DisposableOrderService Disposed:{this.GetHashCode()}");
    }
}
public void ConfigureServices(IServiceCollection services){
    services.AddTransient<IOrderService, DisposableOrderService>();

    services.AddControllers();
}
[HttpGet]
public int Get([FromServices]IOrderService orderService, [FromServices]IOrderService orderService2){  // 获取了两次
    Console.WriteLine("接口请求处理结束");
    return 1;
}

执行后有两个对象被释放掉了:
image
输出结果有两次释放,每次释放的对象hashcode都不相同,说明每次瞬时注册的服务获取到的实例都是不相同的,且都是在整个请求结束后才会去触发释放的;

作用域Scoped服务(重点)

将上述的瞬时注册改为作用域Scoped注册:

//services.AddTransient<IOrderService, DisposableOrderService>();
services.AddScoped<IOrderService>(p => new  DisposableOrderService());
[HttpGet]
public int Get([FromServices]IOrderService orderService, [FromServices]IOrderService orderService2){
    Console.WriteLine("========1========");
    // HttpContext.RequestServices 表示当前请求的容器,是应用程序根容器的一个子容器,每个请求都会创建一个容器
    // 我们在这个子容器下再创建一个子容器scope来获取我们的服务
    using(IServiceScope scope = HttpContext.RequestServices.CreateScope()){   // 通过 HttpContext.RequestServices 创建 Scoped
        var service = scope.ServiceProvider.GetService<IOrderService>();
        //var service2 = scope.ServiceProvider.GetService<IOrderService>();   // 即使你加上这句,输出结果还是2条释放
    }
    Console.WriteLine("========2========");
    Console.WriteLine("接口请求处理结束");
    return 1;
}

image
每次请求都获取到两个释放,意味我们每创建一个Scope的作用域,每个作用域内我们可以是单例的;

单例Singleton服务

将上述的瞬时注册改为单例Singleton注册:

//services.AddTransient<IOrderService, DisposableOrderService>();
services.AddSingleton<IOrderService>(new  DisposableOrderService());
//services.AddScoped<IOrderService>(p => new  DisposableOrderService());
[HttpGet]
public int Get([FromServices]IOrderService orderService, [FromServices]IOrderService orderService2){
    Console.WriteLine("========1========");
    // HttpContext.RequestServices 表示当前请求的根容器,是应用程序根容器的一个子容器,每个请求都会创建一个容器
    // 我们在这个子容器下再创建一个子容器来获取我们的服务
    using(IServiceScope scope = HttpContext.RequestServices.CreateScope()){
        var service = scope.ServiceProvider.GetService<IOrderService>();
        var service2 = scope.ServiceProvider.GetService<IOrderService>();
    }
    Console.WriteLine("========2========");
    return 1;
}

image
发现单例模式是没有释放的;

当我们使用单例时,其服务的实例是我们自己创建的话:

var servi = new  DisposableOrderService();
services.AddSingleton<IOrderService>(servi);
[HttpGet]
public int Get([FromServices]IOrderService orderService, 
        [FromServices]IOrderService orderService2,
		[Fromservices]IHostApplicationLifetime hostApplicationLifetime,
		[FromQuery] stop){
    Console.WriteLine("========1========");
    // HttpContext.RequestServices 表示当前请求的根容器,是应用程序根容器的一个子容器,每个请求都会创建一个容器
    // 我们在这个子容器下再创建一个子容器来获取我们的服务
    using(IServiceScope scope = HttpContext.RequestServices.CreateScope()){
        var service = scope.ServiceProvider.GetService<IOrderService>();
        var service2 = scope.ServiceProvider.GetService<IOrderService>();
    }
    Console.WriteLine("========2========");

    if(stop) hostApplicationLifetime.StopApplication();   // 停止应用程序
    return 1;
}

上述代码运行时,当我们触发停止应用程序后,我们是不会得到任何释放的输出,因为其创建的实例不是由容器来完成的,容器自然也不会管理到其释放(不会去管理对象生命周期的);
image
但是改成如下后:

services.AddSingleton<IOrderService, DisposableOrderService>();

image
当我们触发停止应用程序后,我们得到了一个释放的输出;

当我们注册一个瞬时服务:

public void ConfigureService(IServiceCollection services){
    services.AddTransient<IOrderService, DisposableOrderService>();
}

同时,在 Configure 方法中 从根容器获取瞬时服务时:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env){
    // 从根容器获取瞬时服务
    var s = app.ApplicatonServices.GetService<IOrderService>();
    ...
}

这意味着我们会在根容器去持续的创建我们的IOrderService,但是由于根容器只会在应用程序整个退出时回收,也就意味着我们的这些对象会直积累在我们的应用程序内;
上述代码运行后,只有在项目退出时才会释放回收;
即:我们实现了IDisposable接口的服务,如果是注册瞬时的,我们又在根容器去做操作,它会一直保持到我们的应用程序退出的时候才能够被回收掉,所以说我们瞬时的服务,如果我们是实现了IDisposable的接口,就不建议使用根容器来去创建我们的对象;

标签:容器,释放,作用域,创建,services,IOrderService,时机,public
来源: https://www.cnblogs.com/abc1069/p/16211833.html