imessagesender等接口必须注册,否则di失败;生命周期错配会导致状态泄漏或内存问题;构造函数注入失败主因是未注册接口、注册实现未映射接口、类型拼写错误;手动new绕过di破坏生命周期管理与可测试性;idisposable类的生命周期必须与其注册方式严格匹配。

IMessageSender 这类接口必须存在,否则 IServiceCollection 注册和构造函数注入都会失败——没有抽象契约,DI 就失去意义。
注册服务时选错生命周期会引发状态泄漏或内存问题
常见错误是把本该 Scoped 的数据库上下文(如 DbContext)注册成 Singleton,导致多请求共享同一实例,出现并发修改异常或脏数据;反过来,把日志器(如 ILogger<t></t>)注册为 Transient 会造成不必要的对象创建开销。
-
AddScoped<imyservice myservice>()</imyservice>:适用于一次 HTTP 请求内需复用的资源(如 EF Core 的DbContext) -
AddSingleton<imyservice myservice>()</imyservice>:适合无状态、线程安全的工具类(如配置读取器、缓存客户端) -
AddTransient<imyservice myservice>()</imyservice>:仅用于轻量、无共享状态的类(如 DTO 映射器),避免在高并发下成为瓶颈
注意:AddSingleton<myservice>()</myservice> 和 AddSingleton<imyservice myservice>()</imyservice> 行为不同——前者注册的是具体类型,后者才能通过接口解析。
构造函数注入失败的三个典型原因
运行时报 InvalidOperationException: Unable to resolve service for type 'IMessageSender',基本就这三类:
- 接口没在
Program.cs中调用Add{Transient/Scoped/Singleton}注册(最常见) - 注册了实现类但没注册对应接口,比如只写了
services.AddSingleton<emailsender>()</emailsender>,却试图注入IMessageSender - 构造函数参数名或类型拼写错误,比如把
IMessageSender写成IMessageSendr,编译能过但运行时找不到匹配项
检查方法:在 Program.cs 启动后加一行 app.Services.GetRequiredService<imessagesender>();</imessagesender>,提前暴露解析失败点。
手动 new 实例 + 注入 = 绕过 DI 容器,等于白干
有人在控制器里写 var service = new NotificationService(new EmailSender());,这完全绕过了容器管理,生命周期不受控,IDisposable 不会被自动释放,测试也无法 Mock 替换依赖。
- 正确做法:所有依赖都从构造函数参数进,由容器统一提供
- 如果某类确实需要运行时才决定实现(比如根据用户配置选
EmailSender或SmsSender),应注册一个工厂接口IMessageSenderFactory,再由它返回具体实例 - 绝对不要在服务内部
new其他服务——这会让单元测试无法隔离,也违反了“依赖倒置”原则
IDisposable 实现类的生命周期必须匹配注册方式
一个实现了 IDisposable 的类,如果注册为 Transient,它的 Dispose() 会在每次作用域结束时被调用;若注册为 Singleton,则只在应用关闭时调用一次。错配会导致资源未释放或过早释放。
例如 ScopedDisposable 类注册成 Singleton,它的 Dispose() 可能永远不被触发;反过来,SingletonDisposable 注册成 Transient,每次请求都新建又销毁,违背单例本意。
真正容易被忽略的是:只要类实现了 IDisposable,就必须确保其注册的生命周期与实际使用场景一致——这不是可选项,是容器自动行为的硬约束。










