ASP.NET Core - 依赖注入(三)

devtools/2025/1/17 11:55:18/

ASP.NET Core - 依赖注入(三)

  • 4. 容器中的服务创建与释放

4. 容器中的服务创建与释放

我们使用了 IoC 容器之后,服务实例的创建和销毁的工作就交给了容器去处理,前面也讲到了服务的生命周期,那三种生命周期中对象的创建和销毁分别在什么时候呢。以下面的例子演示一下:

首先是新增三个类,用于注册三种不同的生命周期:

public class Service1
{public Service1(){Console.WriteLine("Service1 Created");}
}
public class Service2
{public Service2(){Console.WriteLine("Service2 Created");}
}
public class Service3
{public Service3(){Console.WriteLine("Service3 Created");}
}

接下来是演示场景,为了简单起见,就用后台服务程序吧

IHost host = Host.CreateDefaultBuilder(args).ConfigureServices(services =>{services.AddHostedService<Worker>();services.AddSingleton<Service1>();services.AddScoped<Service2>();services.AddTransient<Service3>();}).Build();await host.RunAsync();public class Worker : BackgroundService
{private readonly ILogger<Worker> _logger;private readonly IServiceProvider _serviceProvidpublic Worker(ILogger<Worker> logger, IServiceProvider serviceProvider){_logger = logger;_serviceProvider = serviceProvider;}protected override async Task ExecuteAsync(CancellationToken stoppingToken){#region 生命周期实例创建Console.WriteLine("Service1 第一次调用");var service11 = _serviceProvider.GetService<Service1>();Console.WriteLine("Service1 第二次调用");var service12 = _serviceProvider.GetService<Service1>();// 创建作用域,与 Web 应用中的一次请求一样using (var scope = _serviceProvider.CreateScope()){Console.WriteLine("Service2 第一次调用");var service31 = scope.ServiceProvider.GetService<Service2>();Console.WriteLine("Service2 第二次调用");var service32 = scope.ServiceProvider.GetService<Service2>();using (var scope1 = _serviceProvider.CreateScope()){Console.WriteLine("Service2 第三次调用");var service33 = scope1.ServiceProvider.GetService<Service2>();}}{Console.WriteLine("Service3 第一次调用");var service41 = _serviceProvider.GetService<Service3>();Console.WriteLine("Service3 第二次调用");var service42 = _serviceProvider.GetService<Service3>();}#endregion}}
}

最终的输出如下:

在这里插入图片描述
通过输出,我们可以单例生命周期服务在第一次使用的时候创建,之后一直是一个实例,作用域生命周期服务在一个作用域中第一次使用的时候创建实例,之后在同一个实例中只保持一个,但在其他作用域中则会重新创建,而瞬时生命周期服务每次都会创建一个新实例。

看完创建,我们再看实例销毁的时机。

若服务实现了IDisposable接口,并且该服务是由DI容器创建的,则我们不应该手动去Dispose,DI容器会对服务自动进行释放。这里由两个关键点,一个是要实现 Idisposable 接口,一个是由容器创建。这里再增加多两个类,用于演示,并且为了避免干扰将之前演示创建过程的代码注释。

public class Service1 : IDisposable
{public Service1(){Console.WriteLine("Service1 Created");public void Dispose(){Console.WriteLine("Service1 Dispose");}
}public class Service2 : IDisposable
{public Service2(){Console.WriteLine("Service2 Created");public void Dispose(){Console.WriteLine("Service2 Dispose");}
}public class Service3 : IDisposable
{public Service3(){Console.WriteLine("Service3 Created");}public void Dispose(){Console.WriteLine("Service3 Dispose");}
}public class Service4 : IDisposable
{public void Dispose(){Console.WriteLine("Service4 Dispose");}
}public class Service5 : IDisposable
{public void Dispose(){Console.WriteLine("Service5 Dispose");}
}

之后后台服务程序也做一些修改

IHost host = Host.CreateDefaultBuilder(args).ConfigureServices(services =>{services.AddHostedService<Worker>();services.AddSingleton<Service1>();services.AddScoped<Service2>();services.AddTransient<Service3>();// 这种方式依旧由容器创建实例,只不过提供了工厂方法services.AddSingleton<Service4>(provider => new Service4());// 这种方式是用外部创建实例,只有单例生命周期可用services.AddSingleton<Service5>(new Service5());}).Build();await host.RunAsync();public class Worker : BackgroundService
{private readonly ILogger<Worker> _logger;private readonly IServiceProvider _serviceProvidpublic Worker(ILogger<Worker> logger, IServiceProvider serviceProvider){_logger = logger;_serviceProvider = serviceProvider;}protected override async Task ExecuteAsync(CancellationToken stoppingToken){#region 生命周期实Console.WriteLine("Service1 调用");var service1 = _serviceProvider.GetService<Service1>();// 创建作用域,与 Web 应用中的一次请求一样using (var scope = _serviceProvider.CreateScope()){Console.WriteLine("Service2 调用");var service2 = scope.ServiceProvider.GetService<Service2>();Console.WriteLine("即将结束作用域");Console.WriteLine("Service3 调用");var service3 = scope.ServiceProvider.GetService<Service3>();}Console.WriteLine("Service4 调用");var service4 = _serviceProvider.GetService<Service4>();Console.WriteLine("Service5 调用");var service5 = _serviceProvider.GetService<Service5>();#endregion}
}

这样要直接用命令启动应用,不能够通过vs调试,之后Ctrl+C停止应用的时候,输出如下:

在这里插入图片描述
通过输出可以看得到,瞬时生命周期服务和作用域生命周期服务在超出作用范围就会被释放,而单例生命周期服务则在应用关闭时才释放,同为单例生命周期的Service5没有被释放。

这里要提一下的是,在解析瞬时生命周期服务Service3的时候,示例代码中是放到一个单独的作用域中的,这是因为在通过 services.AddHostedService<Worker>(); 注入Worker的时候是注入为单例生命周期的,而在单例生命周期对象中解析其他生命周期的对象是会有问题的,这也是服务注入、解析需要注意的一个关键点。

一定要注意服务解析范围,不要在 Singleton 中解析 Transient 或 Scoped 服务,这可能导致服务状态错误(如导致服务实例生命周期提升为单例,因为单例生命周期的服务对象只会在应用停止的时候释放,而单例对象都没释放,它的依赖项肯定不会释放)。允许的方式有:

  • 在 Scoped 或 Transient 服务中解析 Singleton 服务
  • 在 Scoped 或 Transient 服务中解析 Scoped 服务(不能和前面的Scoped服务相同)

如果要在单例生命周期示例中临时解析作用域、瞬时生命周期的服务,可以通过创建一个子作用域的方式。对子作用域 IServiceScope 的工作方式感兴趣的,可阅读一下这篇文章:细聊.Net Core中IServiceScope的工作方式 。



参考文章:
ASP.NET Core 依赖注入 | Microsoft Learn
理解ASP.NET Core - 依赖注入(Dependency Injection)



ASP.NET Core 系列:

目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core — 依赖注入(二)
下一篇:ASP.NET Core — 依赖注入(四)


http://www.ppmy.cn/devtools/151265.html

相关文章

【深度学习】Pytorch:自实现残差网络

ResNet&#xff08;残差网络&#xff09;是由何凯明等人在2015年发表的论文《深度残差学习用于图像识别》中提出的一种开创性深度学习架构。它在ILSVRC 2015分类任务中获胜&#xff0c;并解决了深度神经网络中的退化问题&#xff0c;使得训练数百甚至数千层的网络成为可能。 残…

用 Python 从零开始创建神经网络(二十):模型评估

模型评估 引言 引言 在第11章《测试或样本外数据》中&#xff0c;我们讨论了验证数据和测试数据之间的区别。对于目前的模型&#xff0c;我们在训练过程中进行了验证&#xff0c;但目前没有一个好的方法来对测试数据运行测试或进行预测。首先&#xff0c;我们将在Model类中添加…

C#局部函数 VS Lambda表达式

一、引言 在 C# 的编程世界里&#xff0c;我们常常会遇到各种实现功能的方式&#xff0c;其中 Lambda 表达式和局部函数都是非常强大的特性。Lambda 表达式自诞生以来&#xff0c;凭借其简洁的语法和强大的功能&#xff0c;深受广大开发者的喜爱&#xff0c;尤其是在处理集合操…

springboot中创建自定义注解和AOP

一、自定义注解的创建 在 Java 中&#xff0c;自定义注解使用 interface 关键字来定义。例如&#xff0c;我们创建一个名为 LogExecutionTime 的自定义注解&#xff0c;用于标记需要记录执行时间的方法&#xff1a; import java.lang.annotation.ElementType; import java.lan…

[NOIP2007 提高组] 矩阵取数游戏

[NOIP2007 提高组] 矩阵取数游戏 显示标签 题目讨论 题目统计 全部提交 时间限制&#xff1a;C/C 1000MS&#xff0c;其他语言 2000MS 内存限制&#xff1a;C/C 256MB&#xff0c;其他语言 512MB 难度&#xff1a;提高/省选- 分数&#xff1a;100 描述 帅帅经常跟同学玩一…

【Sharding-JDBC学习】读写分离_shardjdbc5 不支持 shardingdatasource

8.读写分离 8.1 理解读写分离 面对日益增加的系统访问量&#xff0c;数据库的吞吐量面临着巨大瓶颈。 对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说&#xff0c;将数据库拆分为主库和从库&#xff0c;主库负责处理事务性的增删改操作&#xff0c;从库负责处理…

【计算机网络】lab8 DNS协议

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;计算机网络_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2.…

PyTorch框架——基于深度学习YOLOv11神经网络路面坑洞检测系统

基于深度学习YOLOv11神经网络路面坑洞检测系统&#xff0c;其能识别路面坑洞&#xff0c;见如下 第一步&#xff1a;YOLOv11介绍 YOLOv11是由Ultralytics公司开发的新一代目标检测算法&#xff0c;它在之前YOLO版本的基础上进行了显著的架构和训练方法改进。以下是YOLOv11的一…