作用域和对象释放行为

本文探讨了依赖注入中根容器和子容器的概念,重点解释了DI如何处理实现了IDisposable接口的对象实例释放,强调DI只负责由其创建的实例。建议避免在根容器中获取瞬时服务,并推荐使用容器管理对象生命周期。同时,文章提到了ASP.NET Core中的RequestService和CreateScope创建子容器的方式。总结指出,单例模式在程序关闭时释放,作用域模式在请求结束时释放,而容器只管理实现了IDisposable的服务释放。

一、根容器和子容器

1.根容器

根容器是由通过ServiceCollection Build出的容器。

2.子容器

子容器是由容器创建出来的,有如下两种方式来创建。
第一:RequestService是指ASP.NET Core框架为每个HTTP请求创建的子容器,就是当前请求的容器。
代码如下(示例):

 using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                Debug.WriteLine($"service 's hashcode={service.GetHashCode()}");
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
                Debug.WriteLine($"service1 's hashcode={service1.GetHashCode()}");
            }

第二:使用IServiceprovider的扩展方法CreateScope也可以来创建子容器。

二、实现 IDisposable 接口类型的释放

1.DI 只负责释放由其创建的对象实例

普通方式比如:

services.AddSingleton<IClientService, ClientService>();

工厂注册方式比如:

 var service = new ClientService();
            services.AddSingleton<IClientService>(serviceProvider =>
            {
                return service;
            });

代码如下(示例):
单例:

  /// <summary>
        /// 单例模式,就一个对象,每次都是得到的同一个对象,不会释放;只有程序关闭的时候才会释放
        /// </summary>
        /// <param name="clientService1"></param>
        /// <param name="clientService2"></param>
        /// <returns></returns>
        public int GetSingleServiceLifetime([FromServices] IClientService clientService1, [FromServices] IClientService clientService2)
        {
            using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
            }
            Debug.WriteLine("Done");
            return 0;
        }

Transient:

 /// <summary>
        /// 每次都是一个不同的对象,会释放
        /// </summary>
        /// <param name="clientService1"></param>
        /// <param name="clientService2"></param>
        /// <returns></returns>
        public int GetTransientServiceLifetime([FromServices] IClientService clientService1, [FromServices] IClientService clientService2)
        {
            using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
            }
            Debug.WriteLine("Done");
            return 0;
        }

Result如下:
在这里插入图片描述
Scope:

  /// <summary>
        /// 每个作用域内只能获取相同的对象,作用域释放的时候,再释放对象
        /// </summary>
        /// <param name="clientService1"></param>
        /// <param name="clientService2"></param>
        /// <returns></returns>
        public int GetScopeServiceLifetime([FromServices] IClientService clientService1, [FromServices] IClientService clientService2)
        {
            using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
            }
            Debug.WriteLine("Done");
            return 0;
        }

在这里插入图片描述

2.DI 不负责释放不是由其创建的对象实例

比如注册方式:

services.AddSingleton<IClientService>(new ClientService());

代码如下(示例):

 public int GetServiceLifetime([FromServices]IClientService clientService, [FromServices]IHostApplicationLifetime hostApplicationLifetime,[FromQuery]bool stop =false)
        {
            using (IServiceScope serviceScope = HttpContext.RequestServices.CreateScope())
            {
                var service = serviceScope.ServiceProvider.GetService<IClientService>();
                Debug.WriteLine($"service 's hashcode={service.GetHashCode()}");
                var service1 = serviceScope.ServiceProvider.GetService<IClientService>();
                Debug.WriteLine($"service1 's hashcode={service1.GetHashCode()}");
            }
            if (stop)
            {
                hostApplicationLifetime.StopApplication();
            }
            Debug.WriteLine($"clientService 's hashcode={clientService.GetHashCode()}");
            Debug.WriteLine("Done");
            return 0;
        }

只有当stop=true,程序关闭的时候才会释放的。

三、建议

1.避免在根容器获取实现了 IDisposable 接口的瞬时服务

代码如下(示例):

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //从根容器获取瞬时服务,容器不会自动释放,若需要释放只能手动
            //var s1 = app.ApplicationServices.GetService<IClientService>();
            //ClientService s2 = (ClientService)app.ApplicationServices.GetService<IClientService>();
            //s2.Dispose();
            //var s3 = app.ApplicationServices.GetService<IClientService>();//在跟容器获取作用域服务会直接报错
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
        }

2.避免手动创建实现了 IDisposable 对象,应该使用容器来管理其生命周期

四、源码下载

下载

总结

  1. AddSingleton支持手动new一个对象注入到容器,这种方式只有程序关闭的时候才去释放,其他两种方式都不支持new一个对象注入容器。
  2. db一般是scope,因为它用完需要释放还给连接池,容器可以帮你释放,service、repository也是scope,因为它们依赖db;单例模式仅在明确需要全局共享一个实例时使用
  3. 容器只会管理实现了IDisposable的服务的释放(调用Dispose方法),对于未实现IDisposable的服务,容器是不管的。另外对象最终的回收,是由GC完成的。生命周期的定义,是决定了容器是返回一个新对象,还是复用之前创建的对象。
  4. IServiceCollection表示定义的服务类型、构造方式和生命周期的配置表,IServiceprovider则表示根容器,当调用GetService时,它会根据IServiceCollection中的定义来决定如何构造(或者返回一个已有的)服务对象。
  5. 单例模式,则程序退出时释放;作用域模式,则每个作用域结束时释放,也就是每个请求结束时。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值