ASP.NET Core - 依赖注入(三)
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 - 依赖注入(三)的更多相关文章
- # ASP.NET Core依赖注入解读&使用Autofac替代实现
标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...
- [译]ASP.NET Core依赖注入深入讨论
原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...
- ASP.NET Core依赖注入——依赖注入最佳实践
在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...
- ASP.NET Core依赖注入解读&使用Autofac替代实现【转载】
ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac实现和自定义实现扩展方法 3.1 安装Autof ...
- 实现BUG自动检测 - ASP.NET Core依赖注入
我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评. 如果以后有时间的话,我打算写一个系列的[实现BUG自动检测 ...
- asp.net core 依赖注入几种常见情况
先读一篇注入入门 全面理解 ASP.NET Core 依赖注入, 学习一下基本使用 然后学习一招, 不使用接口规范, 直接写功能类, 一般情况下可以用来做单例. 参考https://www.cnblo ...
- 自动化CodeReview - ASP.NET Core依赖注入
自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...
- ASP.NET Core 依赖注入最佳实践——提示与技巧
在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇 ...
- ASP.NET Core依赖注入最佳实践,提示&技巧
分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...
- ASP.NET Core 依赖注入基本用法
ASP.NET Core 依赖注入 ASP.NET Core从框架层对依赖注入提供支持.也就是说,如果你不了解依赖注入,将很难适应 ASP.NET Core的开发模式.本文将介绍依赖注入的基本概念,并 ...
随机推荐
- 干电池升压IC或者干电池升压芯片
1, 干电池升压IC 升压输出3V,3,3V,5V等3V-5V可调 2, 单节锂电池升压IC 升压输出4. ...
- 3D旋转不能对齐,元素边倾斜
1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset=" ...
- cesium加载倾斜摄影,添加billboard并注册点击事件
完整示例代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...
- MassTransit 知多少 | 基于MassTransit Courier实现Saga 编排式分布式事务
Saga 模式 Saga 最初出现在1987年Hector Garcaa-Molrna & Kenneth Salem发表的一篇名为<Sagas>的论文里.其核心思想是将长事务拆分 ...
- 8个Spring事务失效的场景,你碰到过几种?
前言 作为Java开发工程师,相信大家对Spring种事务的使用并不陌生.但是你可能只是停留在基础的使用层面上,在遇到一些比较特殊的场景,事务可能没有生效,直接在生产上暴露了,这可能就会导致比较严重的 ...
- 2、postman调试
Postman接口调试: postman博客参考 Postman是一个API(接口)开发协作平台,其提供了发送请求.检查响应.自动化测试.数据模拟.服务监控.文档分享等一系列与API(接口)开发有关的 ...
- vertica 7.0 使用kafka
config.propertiesclient.id设置为主机名或ip auto.offset.reset=smallest client.id=qas 设置环境变量 kafka_config=&qu ...
- java入门与进阶-P1.1+P1.2
计算机与编程语言 计算机如何解决问题 !-- 首先计算机他是不知道自己需要去做什么的,它需要按照你所说的步骤一步一步进行直到结束 "请给我一杯水" 1.转身走到厨房; 2.找到一个 ...
- Java 进阶P-6.6+P-7.1
接口设计模式 接口 接口是纯抽象类 所有的成员函数都是抽象函数 所有的成员变量都是public static final 接规定了长什么样,但是不管里面有什么 实现接口 类用extends,接口用im ...
- Unity_UIWidgets - 文字图标Icon
Unity_UIWidgets - 文字图标Icon 文字图标Icon 开篇 不吐不快的废话 正题 主题Icon 获取Icon 第一步 第二步 第三步 使用Icon 1.导入至Resources文件夹 ...