ASP.NET Core 依赖注入:生命周期与模块化设计
依赖注入让对象的创建与使用分离,使业务代码更容易测试和替换。ASP.NET Core 内置容器能够覆盖常见场景,但生命周期选择错误会造成并发安全、资源泄漏或作用域捕获问题。
核心原则
- Transient 适合轻量无状态服务,每次解析都会创建新实例。
- Scoped 通常对应一次 Web 请求,DbContext 等工作单元适合这个生命周期。
- Singleton 在应用进程内共享,必须线程安全且不能直接依赖 Scoped 服务。
- 优先构造函数注入并保持依赖数量合理,避免 Service Locator 模式。
- 按业务模块封装 AddXxx 扩展方法,让 Program.cs 保持清晰。
推荐的实践步骤
先为外部能力和业务服务定义窄接口,再在组合根完成实现绑定。需要在 Singleton 后台服务中使用 Scoped 依赖时,通过 IServiceScopeFactory 为每轮任务创建并释放作用域。对 HttpClient 使用 IHttpClientFactory,统一配置超时、处理器和重试策略。
public static IServiceCollection AddOrders(
this IServiceCollection services)
{
services.AddScoped();
services.AddScoped();
services.AddHttpClient();
return services;
}
示例用于说明实现思路,实际项目还应结合所使用的框架版本、部署环境和业务约束进行调整。重要配置要进入版本管理,并在测试环境验证后再发布。
常见误区
- Singleton 直接注入 DbContext,会跨请求共享非线程安全状态。
- 在业务代码中到处调用 IServiceProvider.GetService,会隐藏真实依赖。
- 注册多个实现却没有明确选择规则,可能在运行时获得意外实现。
上线前检查清单
- 确认“Transient 适合轻量无状态服务,每次解析都会创建新实例”已经通过代码审查或运行验证。
- 确认“Scoped 通常对应一次 Web 请求,DbContext 等工作单元适合这个生命周期”已经通过代码审查或运行验证。
- 确认“Singleton 在应用进程内共享,必须线程安全且不能直接依赖 Scoped 服务”已经通过代码审查或运行验证。
- 确认“优先构造函数注入并保持依赖数量合理,避免 Service Locator 模式”已经通过代码审查或运行验证。
- 为失败路径、边界条件和回滚方案准备测试或演练记录。
- 上线后观察错误率、延迟和资源消耗,确认变化符合预期。
总结
ASP.NET Core 依赖注入:生命周期与模块化设计的关键在于把隐含假设变成可执行的约束,并通过测试、监控和复盘持续验证。先从影响最大的真实场景开始,小步调整并保留回滚能力,通常比一次性大范围改造更安全,也更容易积累可复用的工程经验。