Blazor依赖注入模式:为何不推荐直接注入IServiceProvider获取服务?
IServiceProvider 按需获取服务的原因 你提到的方法B本质是服务定位器模式,该模式不符合.NET依赖注入的设计原则,存在多个容易被忽略的隐性弊端:
生命周期紊乱风险
如果MyService是单例服务,注入的IServiceProvider是全局根容器,此时通过它获取的Scoped生命周期服务会被提升为单例,完全违背预设的生命周期规则,会导致内存泄漏、状态串用等极难排查的问题。而构造函数注入模式下,框架会在启动阶段就校验生命周期依赖的合法性,直接抛出错误阻止异常运行。依赖关系不透明,可维护性差
构造函数显式声明依赖是DI的核心原则,外部可以一眼看出类的依赖项。而方法B将所有依赖隐藏在类的内部实现中,无论是代码阅读、单元测试还是后续维护都会大幅提升成本:单元测试时需要为IServiceProvidermock所有可能用到的服务,无法通过构造函数直观得知需要准备哪些依赖;后续维护人员修改代码时也很容易忽略隐藏的依赖项,引发线上问题。
你提到的「依赖多避免冗长构造函数」其实是代码坏味道的信号:构造函数参数过多说明类的职责过于臃肿,应该优先拆分类的职责,而不是通过服务定位器掩盖问题。错误延后触发,稳定性差
方法A在应用启动、服务首次实例化时就会校验所有依赖是否存在、生命周期是否合法,问题可以在发布前就暴露。而方法B只有运行到对应GetService代码行时才会触发服务不存在、空引用等错误,问题会留到用户实际使用对应功能时才爆发,严重影响线上稳定性。即使改用GetRequiredService避免空引用,错误触发时机也远晚于构造函数注入。不符合设计规范,框架优化失效
.NET DI框架针对构造函数注入做了大量编译和运行时优化,服务定位器模式无法享受到这些优化,同时也不符合官方约定的编码规范,团队协作时容易出现统一标准的问题。
方法B的适用场景
只有在遇到循环依赖、需要按需延迟初始化重量级服务等极少数特殊场景时,才可以有限度使用IServiceProvider获取服务,常规业务开发都应该优先使用构造函数注入。
内容的提问来源于stack exchange,提问作者neggenbe

