在 ASP.NET Core 的依赖注入(Dependency Injection, DI)容器中,Singleton、Transient 和 Scoped 是三种核心生命周期管理方式,用来控制服务实例的创建与共享规则。
理解这三种模式,是掌握 ASP.NET Core 架构设计的关键基础。
一、代码示例
下面用一个简单的示例来说明它们之间的区别:
1. 控制器调用
[ApiController]
[Route("api/[controller]")]
public class TestController : ControllerBase
{
private readonly IServiceProvider _serviceProvider;
public TestController(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
[HttpGet]
public IActionResult Get()
{
Console.WriteLine("======= 收到 API 请求 =======");
var my1 = _serviceProvider.GetService<IMyService>();
var my2 = _serviceProvider.GetService<IMyService>();
my1.DoSomething();
my2.DoSomething();
Console.WriteLine("======= 结束 API 请求 =======\n\n");
return Ok();
}
}
2. 服务定义
public interface IMyService
{
void DoSomething();
}
public class MyService : IMyService
{
private readonly string _instanceId;
public MyService()
{
_instanceId = Random.Shared.Next(100, 999).ToString();
Console.WriteLine($"[MyService-构造函数] 实例 Id 为: {_instanceId}");
}
public void DoSomething()
{
Console.WriteLine($"[MyService-方法调用] 实例 Id 为: {_instanceId}");
}
}
3. 注册方式
// 使用 Singleton 生命周期的示例
builder.Services.AddSingleton<IMyService, MyService>();
// 使用 Scoped 生命周期的示例
builder.Services.AddScoped<IMyService, MyService>();
// 使用 Transient 生命周期的示例
builder.Services.AddTransient<IMyService, MyService>();
二、运行结果
现在,我们来看看它们在不同的场景中的行为:
1. Singleton(单例)
//**********第一次调用API**********
======= 收到 API 请求 =======
[MyService-构造函数] 实例 Id 为: 178
[MyService-方法调用] 实例 Id 为: 178
[MyService-方法调用] 实例 Id 为: 178
======= 结束 API 请求 =======
//**********第二次调用API**********
======= 收到 API 请求 =======
[MyService-方法调用] 实例 Id 为: 178
[MyService-方法调用] 实例 Id 为: 178
======= 结束 API 请求 =======
//**********第三次调用API**********
======= 收到 API 请求 =======
[MyService-方法调用] 实例 Id 为: 178
[MyService-方法调用] 实例 Id 为: 178
======= 结束 API 请求 =======
- 全局只创建一个实例
- 整个应用程序生命周期内共享
- 所有请求都使用同一个对象
👉 特点:全局唯一
2. Scoped (作用域)
//**********第一次调用API**********
======= 收到 API 请求 =======
[MyService-构造函数] 实例 Id 为: 438
[MyService-方法调用] 实例 Id 为: 438
[MyService-方法调用] 实例 Id 为: 438
======= 结束 API 请求 =======
//**********第二次调用API**********
======= 收到 API 请求 =======
[MyService-构造函数] 实例 Id 为: 674
[MyService-方法调用] 实例 Id 为: 674
[MyService-方法调用] 实例 Id 为: 674
======= 结束 API 请求 =======
//**********第三次调用API**********
======= 收到 API 请求 =======
[MyService-构造函数] 实例 Id 为: 365
[MyService-方法调用] 实例 Id 为: 365
[MyService-方法调用] 实例 Id 为: 365
======= 结束 API 请求 =======
- 在同一个请求(Scope)内共享一个实例
- 不同请求之间是不同实例
- 常见于 Web API 请求生命周期
👉 特点:请求内单例,请求间隔离
3.Transient(瞬态)
//**********第一次调用API**********
======= 收到 API 请求 =======
[MyService-构造函数] 实例 Id 为: 794
[MyService-方法调用] 实例 Id 为: 794
[MyService-构造函数] 实例 Id 为: 182
[MyService-方法调用] 实例 Id 为: 182
======= 结束 API 请求 =======
//**********第二次调用API**********
======= 收到 API 请求 =======
[MyService-构造函数] 实例 Id 为: 436
[MyService-方法调用] 实例 Id 为: 436
[MyService-构造函数] 实例 Id 为: 588
[MyService-方法调用] 实例 Id 为: 588
======= 结束 API 请求 =======
//**********第三次调用API**********
======= 收到 API 请求 =======
[MyService-构造函数] 实例 Id 为: 391
[MyService-方法调用] 实例 Id 为: 391
[MyService-构造函数] 实例 Id 为: 182
[MyService-方法调用] 实例 Id 为: 182
======= 结束 API 请求 =======
- 每次请求都会创建一个新实例
- 每次解析(Resolve)都会生成新的对象
- 不共享状态
👉 特点:用完即弃,每次都是新的
三、区别总结
| 生命周期 | 创建次数 | 是否共享 | 适用场景 |
|---|---|---|---|
| Singleton | 全局一次 | 全局共享 | 配置、缓存、日志 |
| Scoped | 每个请求一次 | 请求内共享 | DB上下文、业务服务 |
| Transient | 每次解析一次 | 不共享 | 无状态工具类 |
四、使用建议
✔ 推荐使用方式
- 默认优先:
Scoped - 无状态工具:
Transient - 全局共享且线程安全:
Singleton
⚠️ 常见坑
- ❌ 在 Singleton 中注入 Scoped(容易导致作用域失效)
- ❌ Singleton 持有可变状态(线程安全风险)
- ❌ 过度使用 Transient(性能浪费)
五、总结理解
- Singleton:整个应用只有一个
- Scoped:每个请求一个
- Transient:每次用都是新的
Top comments (0)