.NET Core作用域与对象释放行为详解

简介: 上节,我们提到,容器中有3种不同生命周期,分别是Singleton、Scoped和Transient。那么,你知道这3种类型的对象的释放时机和坑吗?

作用域核心类型

IServiceScope接口


实现IDisposable接口类型的释放

  • DI只负责释放由其创建的对象的实例
  • DI在容器或子容器释放时,释放由其创建的对象实例


下面,我们主要通过代码演示来理解这3种情况。


代码演示

首先,我们定义一个方法,用fromServices来获取对象2次,以用于对比是否有新的对象产生

[HttpGet]public int GetService([FromServices] IOrderService orderService1,[FromServices] IOrderService orderService2){    Console.WriteLine("=======创建子容器对象========");     using (IServiceScope scope = HttpContext.RequestServices.CreateScope())    {        var service = scope.ServiceProvider.GetService<IOrderService>();        var service1 = scope.ServiceProvider.GetService<IOrderService>();        var service2 = scope.ServiceProvider.GetService<IOrderService>();    }    Console.WriteLine("============子容器对象释放完成===========");    Console.WriteLine("接口处理结束");     return 1;}


Scoped

先以Scoped类型为例,我们在ConfigureService里添加Scoped类型的对象


services.AddScoped<IOrderService, OrderService>();


运行程序后,我们观察下输出

=======创建子容器对象========
DisposableOrderService Disposabled:38716773
============子容器对象释放完成===========
接口处理结束
DisposableOrderService Disposabled:3768695


从以上可以看出,我们在方法内的using部分成功创建了Scoped对象,该对象在using结束后已经得到释放,我们需要注意的是第二行的disposable输出,从该行位置可以看出,我们在ConfigureService中注册的对象,在接口方法执行完毕后才得到释放,与Scoped模式的定义相符。

Singleton

其次,我们来注册一个Singleton对象试试,根据我们的预测应该是没有任何的释放输出的,因为,Singleton对象在根容器释放时才会释放


services.AddSingleton<IOrderService, OrderService>();


结果如下,和期望一致。


=======创建子容器对象========
============子容器对象释放完成===========
接口处理结束


Transient

我们在来看Transient模式


services.AddTransient<IOrderService, OrderService>();


根据我们的判断,瞬时模式,每次都会获取一个新的对象,那么这里,应该一共会有5次释放动作。运行结果如下

=======创建子容器对象========
DisposableOrderService Disposabled:36550596
DisposableOrderService Disposabled:50947826
DisposableOrderService Disposabled:28203839
============子容器对象释放完成===========
接口处理结束
DisposableOrderService Disposabled:12451586
DisposableOrderService Disposabled:59782618


这里我们可以看出,在using期间创建了3个Transient对象,并最终全部得到释放。

Singleton释放时机验证

上面我们提到,Singleton对象在根容器退出时才会释放,那么,我们如何确定其是否释放呢?我们可以通过IHostApplicationLifetime来结束程序的生命周期,以判断其输出是否包含对Singleton的释放行为,我们定义如下方法


[HttpGet]
public void appExit([FromServices] IHostApplicationLifetime hostApplicationLifetime,[FromQuery]bool stop)
{
    if (stop)
    {
        hostApplicationLifetime.StopApplication();
    }
    //return 1;
}


并在执行上述方法之后,再通过浏览器访问该方法来结束程序,在访问地址后加stop=true参数即可模拟程序退出。如


http://localhost:5000/weatherforecast/appexit?stop=true


这里,我们先看看单例下的执行结果


=======创建子容器对象========
============子容器对象释放完成===========
接口处理结束
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
DisposableOrderService Disposabled:62103957


从这里,我们可以看出,Singleton对象在程序退出时得到了释放其实,Scoped和Transient模式的注册,释放行为都是在对象释放时,而不是程序退出时,如下


=======创建子容器对象========
DisposableOrderService Disposabled:33592719
============子容器对象释放完成===========
接口处理结束
DisposableOrderService Disposabled:18075529
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...


避坑指南

既然Transient每次都会创建一个新的对象,那么我们就要避免在根容器内获取Transient的对象,因为这些对象会一直保留直到根容器退出时才会释放,会导致程序的性能问题如下,若在Configure内定义如下语句


var service = app.ApplicationServices.GetService<IOrderService>();
var service1 = app.ApplicationServices.GetService<IOrderService>();
var service2 = app.ApplicationServices.GetService<IOrderService>();


则这3个对象的退出如下


info: Microsoft.Hosting.Lifetime[0]
Application is shutting down...
DisposableOrderService Disposabled:49538252
DisposableOrderService Disposabled:62696216
DisposableOrderService Disposabled:31071611


建议

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

这里在演示一下手动创建对象的情况


IOrderService order = new OrderService();
services.AddSingleton<IOrderService>(order);


直接调用退出程序的方法,得到如下结果,可以看出并没有释放手动创建的对象


info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...


本节内容到此结束,下一节,介绍使用第三方的容器框架Autofac来增强容器能力以及引入切面编程(AOP)的能力。
源码可访问


https://github.com/IronMarmot/Samples/tree/master/CoreSample
相关文章
|
22天前
|
消息中间件 前端开发 小程序
一个基于.NET Core构建的简单、跨平台、模块化的商城系统
今天大姚给大家分享一个基于.NET Core构建的简单、跨平台、模块化、完全开源免费(MIT License)的商城系统:Module Shop。
|
22天前
|
算法 C# 数据库
【干货】一份10万字免费的C#/.NET/.NET Core面试宝典
C#/.NET/.NET Core相关技术常见面试题汇总,不仅仅为了面试而学习,更多的是查漏补缺、扩充知识面和大家共同学习进步。该知识库主要由自己平时学习实践总结、网上优秀文章资料收集(这一部分会标注来源)和社区小伙伴提供三部分组成。该份基础面试宝典完全免费,发布两年来收获了广大.NET小伙伴的好评,我会持续更新和改进,欢迎关注我的公众号【追逐时光者】第一时间获取最新更新的面试题内容。
|
22天前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
|
5天前
|
开发框架 前端开发 JavaScript
JavaScript云LIS系统源码ASP.NET CORE 3.1 MVC + SQLserver + Redis医院实验室信息系统源码 医院云LIS系统源码
实验室信息系统(Laboratory Information System,缩写LIS)是一类用来处理实验室过程信息的软件,云LIS系统围绕临床,云LIS系统将与云HIS系统建立起高度的业务整合,以体现“以病人为中心”的设计理念,优化就诊流程,方便患者就医。
14 0
|
20天前
|
Linux API iOS开发
.net core 优势
.NET Core 的优势:跨平台兼容(Windows, macOS, Linux)及容器支持,高性能,支持并行版本控制,丰富的新增API,以及开源。
25 4
|
2月前
|
开发框架 人工智能 .NET
C#/.NET/.NET Core拾遗补漏合集(持续更新)
C#/.NET/.NET Core拾遗补漏合集(持续更新)
|
2月前
|
开发框架 中间件 .NET
C# .NET面试系列七:ASP.NET Core
## 第一部分:ASP.NET Core #### 1. 如何在 controller 中注入 service? 在.NET中,在ASP.NET Core应用程序中的Controller中注入服务通常使用<u>依赖注入(Dependency Injection)</u>来实现。以下是一些步骤,说明如何在Controller中注入服务: 1、创建服务 首先,确保你已经在应用程序中注册了服务。这通常在Startup.cs文件的ConfigureServices方法中完成。例如: ```c# services.AddScoped<IMyService, MyService>(); //
65 0
|
7月前
|
开发框架 前端开发 .NET
ASP.NET Core 核心特性学习笔记「下」
ASP.NET Core 核心特性学习笔记「下」
|
7月前
|
开发框架 前端开发 中间件
ASP.NET Core 核心特性学习笔记「上」
ASP.NET Core 核心特性学习笔记「上」
|
SQL 机器学习/深度学习 Cloud Native
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记
- 状态 - 自动变更检测 - 不查询删除和更新 - 并发
225 0
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记