关于GetRedirectedTypeOrNull方法与服务注册实例一致性的技术咨询
依赖注入服务注册的重定向逻辑与实例一致性解析
1. 理解GetRedirectedTypeOrNull方法
这个方法是服务注册阶段的实例一致性协调工具,核心作用是从所有要暴露的服务类型中,筛选出一个作为「基准类型」,让其他暴露类型的服务请求都重定向到这个基准类型获取实例。
- 输入:实现类类型、当前要注册的暴露服务类型、所有暴露服务类型的集合
- 输出:存在合规基准类型则返回它,否则返回null(代表无需重定向)
2. Singleton/Scoped服务需要重定向的原因
Singleton(单例)和Scoped(作用域)的设计核心是实例唯一性:
- Singleton要求全局范围内同一服务仅存在一个实例
- Scoped要求同一个作用域内同一服务仅存在一个实例
如果不做重定向,每个暴露的服务类型(比如IA1、IA2)会被单独注册为指向实现类的服务。这会导致:请求IA1时创建一个A实例,请求IA2时又创建另一个A实例,直接违背了Singleton/Scoped的生命周期约定。而Transient(瞬时)服务本身就是每次请求生成新实例,因此不需要重定向。
核心注册逻辑代码
protected virtual ServiceDescriptor CreateServiceDescriptor( Type implementationType, Type exposingServiceType, List<Type> allExposingServiceTypes, ServiceLifetime lifeTime) { if (lifeTime.IsIn(ServiceLifetime.Singleton, ServiceLifetime.Scoped)) { var redirectedType = GetRedirectedTypeOrNull( implementationType, exposingServiceType, allExposingServiceTypes ); if (redirectedType != null) { return ServiceDescriptor.Describe( exposingServiceType, provider => provider.GetService(redirectedType), lifeTime ); } } return ServiceDescriptor.Describe( exposingServiceType, implementationType, lifeTime ); }
- 当生命周期为Singleton/Scoped时,优先尝试获取重定向目标类型
- 若拿到重定向类型,就注册「代理服务」:请求当前暴露类型时,实际从容器中获取重定向类型的实例
- 若无重定向目标,则直接注册暴露类型指向实现类的常规服务
现象解析
现象1:仅暴露IA1和IA2时,获取不同实例
代码示例:
[ExposeServices(typeof(IA1), typeof(IA2))] public class A: IA1, IA2, IScopedDependency
- 此时
allExposingServiceTypes集合仅包含IA1和IA2,没有实现类A本身 GetRedirectedTypeOrNull找不到合适的基准类型,返回null- IA1和IA2会被分别注册为独立的Scoped服务,各自指向实现类A
- 同一作用域内,请求IA1会创建并缓存一个A实例,请求IA2会创建并缓存另一个A实例,因此拿到不同实例
- 关于「A默认也会被暴露」:这是框架默认行为——即使显式指定了暴露接口,框架仍会自动把实现类A本身作为服务注册,但该注册与IA1、IA2的注册相互独立,所以通过A获取的实例和通过IA1/IA2获取的也不是同一个。
现象2:暴露A、IA1和IA2时,获取同一实例
代码示例:
[ExposeServices(typeof(A), typeof(IA1), typeof(IA2))]
- 此时
allExposingServiceTypes集合包含A、IA1、IA2 GetRedirectedTypeOrNull会把实现类A作为基准类型返回- 注册IA1和IA2时,会生成重定向到A的代理服务:请求IA1/IA2时,实际从容器中获取A的实例
- 由于A是Scoped服务,同一作用域内只会存在一个A实例,因此通过A、IA1、IA2获取到的都是这个实例,保证了一致性
内容的提问来源于stack exchange,提问作者xiaolipro
相关产品推荐
相关产品推荐

