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

embedded/2025/1/19 4:04:27/

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/embedded/155133.html

相关文章

C++实现设计模式--- 观察者模式 (Observer)

观察者模式 (Observer) 观察者模式 是一种行为型设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;使得当一个对象的状态发生改变时&#xff0c;其依赖者&#xff08;观察者&#xff09;会收到通知并自动更新。 意图 定义对象之间的一对多依赖关系。当一个对象状…

PyTorch DAY2: 搭建神经网络

如今&#xff0c;我们已经了解了 PyTorch 中张量及其运算&#xff0c;但这远远不够。本次实验将学会如何使用 PyTorch 方便地构建神经网络模型&#xff0c;以及 PyTorch 训练神经网络的步骤及方法。 知识点 PyTorch 构建神经网络Sequential 容器结构使用 GPU 加速训练模型保存…

JVM:ZGC详解(染色指针,内存管理,算法流程,分代ZGC)

1&#xff0c;ZGC&#xff08;JDK21之前&#xff09; ZGC 的核心是一个并发垃圾收集器&#xff0c;所有繁重的工作都在Java 线程继续执行的同时完成。这极大地降低了垃圾收集对应用程序响应时间的影响。 ZGC为了支持太字节&#xff08;TB&#xff09;级内存&#xff0c;设计了基…

Python从0到100(八十四):神经网络-卷积神经网络训练CIFAR-10数据集

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

CV与NLP经典大模型解读

一。llm与lora微调策略解读 (1)文本大模型 llama:meta开源语言模型(咱们能负担得起下游任务了)。 lora:绘你模型你也得能训练的动才行(咱们也能微调下游任务)。loradiffusion。 self-instruct:下游任务得规矩些&#xff0c;输入与输出都得有一个标准格式。 peft:将上面三个…

Python----Python高级(面向对象:封装、继承、多态,方法,属性,拷贝,组合,单例)

一、封装 隐藏对象的属性和实现细节&#xff0c;只对外提供必要的方法。相当于将“细节封装起来”&#xff0c;只对外暴露“相关调用方法”。 Python追求简洁的语法&#xff0c;没有严格的语法级别的“访问控制符”&#xff0c;更多的是依靠程序员自觉实现。 class BankAccoun…

yolo训练数据集样本的标签形状一致是什么意思

“标签形状一致”指的是每个样本的标签数据在维度和大小上必须是相同的。例如&#xff0c;在目标检测任务中&#xff0c;标签通常包含目标的位置信息&#xff08;例如&#xff0c;[class, x_center, y_center, width, height]&#xff09;&#xff0c;每个目标在图像中的标签应…

密码学——密码学基础、散列函数与数字签名

1.密码学概述 是信息安全的基础和核心&#xff0c;是防范各种安全威胁的重要手段&#xff0c;信息安全的许多相关知识都与密码学相关。 密码学发展 密码学是一门古老而又年轻的学科 &#xff0c;几千年以前就存在&#xff0c;至今仍在发展演进。地位非常重要甚至起决定性作用…