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

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.mzph.cn/news/892843.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gitlab runner正常连接 提示 作业挂起中,等待进入队列 解决办法

方案1 作业挂起中,等待进入队列 重启gitlab-runner gitlab-runner stop gitlab-runner start gitlab-runner run方案2 启动 gitlab-runner 服务 gitlab-runner start成功启动如下 [rootdocserver home]# gitlab-runner start Runtime platform …

麦田物语学习笔记:构建游戏的时间系统

基本流程 1.代码思路 (1)新建一个TimeManager.cs (2)创建枚举变量来表示四季,在TimeManager里需要的变量有: 游戏内的秒,分钟,小时,天,月,年;游戏内的季节;控制一个季节有多少个月;控制时间的暂停;计时器tikTime (3)在Settings里添加计时器的阈值,以及各个时间的进位 (4)初始化…

Spring Boot教程之五十七:在 Apache Kafka 上发布 JSON 消息

Spring Boot | 如何在 Apache Kafka 上发布 JSON 消息 Apache Kafka是一个发布-订阅消息系统。消息队列允许您在进程、应用程序和服务器之间发送消息。在本文中&#xff0c;我们将了解如何在 Spring Boot 应用程序中向 Apache Kafka 发送 JSON 消息。 为了了解如何创建 Spring…

计算机网络 (44)电子邮件

一、概述 电子邮件&#xff08;Electronic Mail&#xff0c;简称E-mail&#xff09;是因特网上最早流行的应用之一&#xff0c;并且至今仍然是因特网上最重要、最实用的应用之一。它利用计算机技术和互联网&#xff0c;实现了信息的快速、便捷传递。与传统的邮政系统相比&#…

代码随想录算法训练营day02| 977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II

977. 有序数组的平方 双指针&#xff0c;新数组用k&#xff1b; 由于已经排序&#xff0c;所以比较两侧数据即可&#xff1b; class Solution { public:vector<int> sortedSquares(vector<int>& nums) {vector<int> ans(nums.size());int k nums.siz…

Unity 语音转文字 Vosk 离线库

市场有很多语音库&#xff0c;这里介绍Vosk SDK 除了支持untiy外还有原生开发服务器等 目录 安装unity示例demo下载语音训练文件运行demo结尾一键三联 注意事项 有可能debug出来的文本是空的&#xff0c;&#xff08;确保麦克风正常&#xff0c;且索引正确&#xff09;分大…

网络网络层ICMP协议

网络网络层ICMP协议 1. ICMP 协议介绍 ICMP&#xff08;Internet Control Message Protocol&#xff09;是 TCP/IP 协议簇中的网络层控制报文协议。用于在 IP 主机、路由器之间传递控制消息&#xff0c;提供可能有关通信问题的反馈信息。 以及用于网络诊断或调试&#xff08;…

Lianwei 安全周报|2025.1.13

新的一周又开始了&#xff0c;以下是本周「Lianwei周报」&#xff0c;我们总结推荐了本周的政策/标准/指南最新动态、热点资讯和安全事件&#xff0c;保证大家不错过本周的每一个重点&#xff01; 政策/标准/指南最新动态 01 美国国土安全部发布《公共部门生成式人工智能部署手…

计算机网络(五)运输层

5.1、运输层概述 概念 进程之间的通信 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。 当网络的边缘部分中的两个主机使用网络的核心部分的功能进行端到端的通信时…

卷积神经02-CUDA+Pytorch环境安装

卷积神经02-CUDAPytorch环境安装 在使用Python进行pytorch的使用过程中遇到各种各样的版本冲突问题&#xff0c;在此进行记录 0-核心知识脉络 1&#xff09;根据自己电脑的CUDA版本安装对应版本的Pytorch&#xff0c;充分的使用GPU性能2&#xff09;电脑要先安装【CUDA ToolKi…

shell 脚本基本练习

一、shell 脚本写出检测 /tmp/size.log 文件如果存在显示它的内容&#xff0c;不存在则创建一个文件将创建时间写入。 1. 创建ex1.sh文件 [rootopenEuler mnt]# vim ex1.sh创建如下&#xff1a; 2. 根据题目编写脚本 n"/tmp/siz.log"if [ -f "$n" ] thenc…

CNN-GRU-MATT加入贝叶斯超参数优化,多输入单输出回归模型

CNN-GRU-MATT加入贝叶斯超参数优化&#xff0c;多输入单输出回归模型 目录 CNN-GRU-MATT加入贝叶斯超参数优化&#xff0c;多输入单输出回归模型预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现贝叶斯优化CNN-GRU融合多头注意力机制多变量回归预测&#xff…

Oracle 分区索引简介

目录 一. 什么是分区索引二. 分区索引的种类2.1 局部分区索引&#xff08;Local Partitioned Index&#xff09;2.2 全局分区索引&#xff08;Global Partitioned Index&#xff09; 三. 分区索引的创建四. 分区索引查看4.1 USER_IND_COLUMNS 表4.2 USER_INDEXES 表 五. 分区索…

​HPM6700——以太网通信lwip_udpecho_freertos_socket

1. 概述 本示例展示在FreeRTOS系统下的UDP回送通讯 PC 通过以太网发送UDP数据帧至MCU&#xff0c;MCU将接收的数据帧回发至PC 2. 硬件设置 使用USB Type-C线缆连接PC USB端口和PWR DEBUG端口 使用以太网线缆连接PC以太网端口和开发板RGMII或RMII端口 3. 工程配置 以太网端…

STC的51单片机LED点灯基于KEIL

前言&#xff1a; 该文源于回答一个朋友的问题&#xff0c;代码为该朋友上传&#xff0c;略作修改&#xff0c;在此说明问题以及解决问题的思路&#xff0c;以减少新手错误。 电路图&#xff1a; 该位朋友未上传电路图&#xff0c;说明如下&#xff1a; stc8g1k08a-sop8控制…

[leetcode]链表基础回顾

一.创建带头节点的链表 #include <iostream> #include <string> #include <algorithm> using namespace std; typedef struct Node { char ch; Node* next; }*LinkList,ListNode; void printLinkList(LinkList& head) { LinkList p head…

Cesium在vue3中的简单使用

目录 一、介绍 二、创建和基础配置 2.1 使用vite创建vue3项目 2.2 安装所需依赖 2.3 修改配置文件vite.config.js 2.4 配置路由文件 2.5 使用路由文件 三、使用cesium原本的进行渲染 3.1 渲染效果 3.2 代码实现 3.2.1 默认效果代码 3.2.2 空白地图效果代码 3.2.3 修改默…

【Uniapp-Vue3】使用defineExpose暴露子组件的属性及方法

如果我们想要让父组件访问到子组件中的变量和方法&#xff0c;就需要使用defineExpose暴露&#xff1a; defineExpose({ 变量 }) 子组件配置 父组件配置 父组件要通过onMounted获取到子组件的DOM 传递多个属性和方法 子组件 父组件

小米vela系统(基于开源nuttx内核)——openvela开源项目

前言 在 2024 年 12 月 27 日的小米「人车家全生态」合作伙伴大会上&#xff0c;小米宣布全面开源 Vela 操作系统。同时&#xff0c;OpenVela 项目正式上线 GitHub 和 Gitee&#xff0c;采用的是比较宽松的 Apache 2.0 协议&#xff0c;这意味着全球的开发者都可以参与到 Vela…

1.15寒假作业

web&#xff1a;nss靶场ez_ez_php 打开环境&#xff0c;理解代码 使用个体传参的方法&#xff0c;首先代码会检查file参数的前三个字符是不是php&#xff0c;如果是就输出nice&#xff0c;然后用include函数包含file&#xff0c;绕过不是则输出hacker&#xff0c;如果没有file…