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

news2025/1/19 0:09:56

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 _serviceProvid
    public 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 _serviceProvid
    public 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.coloradmin.cn/o/2278695.html

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

相关文章

高通8255 Android STR 启动失败要因分析调查

目录 背景&#xff1a; 调查过程&#xff1a; 步骤1&#xff1a; slog2info | grep vmm_service 步骤2&#xff1a; slog2info | grep qvm 总结&#xff1a; 解决方案 背景&#xff1a; 调试高通8255 STR的STR过程中发现Android和QNX进入STR状态后&#xff0c;脱出STR时…

Linux操作命令之云计算基础命令

一、图形化界面/文本模式 ctrlaltF2-6 图形切换到文本 ctrlalt 鼠标跳出虚拟机 ctrlaltF1 文本切换到图形 shift ctrl "" 扩大 ctrl "-" 缩小 shift ctrl "n" 新终端 shift ctrl "t" 新标签 alt 1,…

LabVIEW桥接传感器配置与数据采集

该LabVIEW程序主要用于配置桥接传感器并进行数据采集&#xff0c;涉及电压激励、桥接电阻、采样设置及错误处理。第一个VI&#xff08;"Auto Cleanup"&#xff09;用于自动清理资源&#xff0c;建议保留以确保系统稳定运行。 以下是对图像中各个组件的详细解释&#…

面试题解析

1、写一个sed命令&#xff0c;修改/tmp/input.txt文件的内容 要求&#xff1a; 删除所有空行&#xff1b; 在非空行前面加一个"AAA"&#xff0c;在行尾加一个"BBB"&#xff0c;即将内容为11111的一行改为&#xff1a;AAA11111BBB 创造测试文件&#xff1a;…

合合信息名片全能王上架原生鸿蒙应用市场,成为首批数字名片类应用

长期以来&#xff0c;名片都是企业商务沟通的重要工具。随着企业数字化转型&#xff0c;相较于传统的纸质名片&#xff0c;数字名片对于企业成员拓展业务、获取商机、提升企业形象等方面发挥着重要作用。近期&#xff0c;合合信息旗下名片全能王正式上线原生鸿蒙应用市场&#…

【日志篇】(7.6) ❀ 01. 在macOS下刷新FortiAnalyzer固件 ❀ FortiAnalyzer 日志分析

【简介】FortiAnalyzer 是 Fortinet Security Fabric 安全架构的基础&#xff0c;提供集中日志记录和分析&#xff0c;以及端到端可见性。因此&#xff0c;分析师可以更有效地管理安全状态&#xff0c;将安全流程自动化&#xff0c;并快速响应威胁。具有分析和自动化功能的集成…

《汽车维修技师》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答&#xff1a; 问&#xff1a;《汽车维修技师》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊。 问&#xff1a;《汽车维修技师》级别&#xff1f; 答&#xff1a;省级。主管单位&#xff1a;北方联合出版传媒&#xff08;…

【OpenCV(C++)快速入门】--opencv学习

0 配置环境 配置环境网上很多资料&#xff0c;这里就不赘述了。 笔者使用的是VS2022opencv4.9.0 测试配置环境 // 打开摄像头样例 #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/imgcodecs/imgcod…

Python编程与在线医疗平台数据挖掘与数据应用交互性研究

一、引言 1.1 研究背景与意义 在互联网技术飞速发展的当下,在线医疗平台如雨后春笋般涌现,为人们的就医方式带来了重大变革。这些平台打破了传统医疗服务在时间和空间上的限制,使患者能够更加便捷地获取医疗资源。据相关报告显示,中国基于互联网的医疗保健行业已进入新的…

网安快速入门之Windows命令

在Windows中 我们今天介绍几个命令&#xff1a; help copy dir cd type del ipconfig net netstat tasklist sc1. help 显示命令的帮助信息。或者显示Windows内置命令。 常用参数&#xff1a; <命令>&#xff1a;查看指定命令的帮助。 示例&#xff1a;help copy 显…

python convert.py -s Rubble

E0222 12:08:50.144686 1326795 logging.cc:56] [option_manager.cc:813] Check failed: ExistsDir(*image_path) E0222 12:08:50.144773 1326795 option_manager.cc:879] Invalid options provided. ERROR:root:Feature extraction failed with code 256. Exiting.在运行pytho…

[BrainShadow-V1] VR头戴设备统计报告

Brain-Shadow-V1 EventVR headsetsReported byXiao enDate2025/01/15Version1.0 HTC Vive Pro 2 Pro HTC Vive Pro 2 是一款高端虚拟现实头显&#xff0c;配备双 2.5K 显示屏&#xff0c;组合分辨率达到 48962448&#xff0c;提供 120 的视场角和 120Hz 的刷新率。该设备支持…

多监控m3u8视频流,怎么获取每个监控的封面图(纯前端)

文章目录 1.背景2.问题分析3.解决方案3.1解决思路3.2解决过程3.2.1 封装播放组件3.2.2 隐形的视频div3.2.3 截取封面图 3.3 结束 1.背景 有这样一个需求&#xff1a; 给你一个监控列表&#xff0c;每页展示多个监控&#xff08;至少12个&#xff0c;m3u8格式&#xff09;&…

Golang Gin系列-2:搭建Gin 框架环境

开始网络开发之旅通常是从选择合适的工具开始的。在这个全面的指南中&#xff0c;我们将引导你完成安装Go编程语言和Gin框架的过程&#xff0c;Gin框架是Go的轻量级和灵活的web框架。从设置Go工作空间到将Gin整合到项目中&#xff0c;本指南是高效而强大的web开发路线图。 安装…

日志收集Day001

1.ElasticSearch 作用&#xff1a;日志存储和检索 2.单点部署Elasticsearch与基础配置 rpm -ivh elasticsearch-7.17.5-x86_64.rpm 查看配置文件yy /etc/elasticsearch/elasticsearch.yml&#xff08;这里yy做了别名&#xff0c;过滤掉空行和注释行&#xff09; yy /etc/el…

SW - 快捷键

文章目录 SW - 快捷键概述笔记视图缩放视图平移视图旋转END SW - 快捷键 概述 鼠标中键不好使了&#xff0c;导致鼠标滚轮的操作在SW中不好使。 已经拆过鼠标&#xff0c;清理过中键计数轮那里的灰尘。只好用几天&#xff0c;然后鼠标中键又不好使了。 先查一下能代替鼠标中键…

Freeswitch使用media_bug能力实现回铃音检测

利用freeswitch的media bug能力来在智能外呼时通过websocket对接智能中心的声音检测接口,来实现回铃音检测,来判断用户当前是否已响应,拒接,关机等。 1.回铃音处理流程 2.模块源码目录结构 首先新建一个freeswitch的源码的src/application目录下新建一个子目录mod_ringba…

【2024年华为OD机试】(B卷,100分)- 恢复数字序列 (Java JS PythonC/C++)

一、问题描述 题目解析 题目描述 对于一个连续正整数组成的序列&#xff0c;可以将其拼接成一个字符串&#xff0c;再将字符串里的部分字符打乱顺序。例如&#xff0c;序列 8 9 10 11 12 拼接成的字符串为 89101112&#xff0c;打乱一部分字符后得到 90811211&#xff0c;原…

ZNS SSD垃圾回收优化方案解读-2

四、Brick-ZNS 关键设计机制解析 Brick-ZNS 作为一种创新的 ZNS SSD 设计&#xff0c;聚焦于解决传统 ZNS SSDs 在垃圾回收&#xff08;GC&#xff09;过程中的数据迁移低效问题&#xff0c;其核心特色为存储内数据迁移与地址重映射功能。在应用场景中&#xff0c;针对如 Rock…

云消息队列 Kafka 版 V3 系列荣获信通院“云原生技术创新标杆案例”

2024 年 12 月 24 日&#xff0c;由中国信息通信研究院&#xff08;以下简称“中国信通院”&#xff09;主办的“2025 中国信通院深度观察报告会&#xff1a;算力互联网分论坛”&#xff0c;在北京隆重召开。本次论坛以“算力互联网 新质生产力”为主题&#xff0c;全面展示中国…