如何为IServiceCollection实现严格模式避免依赖被意外覆盖
为IServiceCollection实现健壮的「严格注册模式」
核心目标
针对多人协作的.NET项目,实现IServiceCollection的「严格模式」:当任何AddXXX扩展方法尝试重复注册相同依赖时,立即抛出异常(或汇总异常),避免依赖被意外覆盖。
现有实现的潜在问题
你当前的实现已经覆盖了基础场景,但存在几个边缘情况需要优化:
- 未考虑生命周期:相同服务类型、实现类型和ServiceKey,但生命周期不同(比如先注册Singleton,再注册Scoped),现有逻辑会判定为不重复,但实际业务中可能需要禁止这种情况
- 工厂注册的服务无法正确判定:当使用工厂方式注册服务(如
AddSingleton<IService>(sp => new Impl())),GetImplementationType()会返回null,现有逻辑会跳过重复检查 - 未覆盖批量修改方法:比如
AddRange、Insert等方法会绕过你的Add方法拦截,直接修改原始集合 - 异常粒度问题:单个重复就抛出异常,无法一次性汇总所有重复注册问题
健壮实现方案
1. 完整包装IServiceCollection
实现一个完整的包装器类,拦截所有可能修改服务集合的方法,而不仅仅是单个Add方法。
public interface IStrictServiceCollection : IServiceCollection { // 切换回普通模式 IServiceCollection AsNormal(); } public class StrictServiceCollection : IStrictServiceCollection { private readonly IServiceCollection _innerCollection; private readonly bool _throwOnFirstViolation; // 控制是立即抛出还是汇总异常 public StrictServiceCollection(IServiceCollection innerCollection, bool throwOnFirstViolation = true) { _innerCollection = innerCollection; _throwOnFirstViolation = throwOnFirstViolation; } // 实现IServiceCollection的所有成员,转发到_innerCollection,除了修改类方法 public int Count => _innerCollection.Count; public bool IsReadOnly => _innerCollection.IsReadOnly; public ServiceDescriptor this[int index] { get => _innerCollection[index]; set => ValidateAndSet(index, value); } public IEnumerator<ServiceDescriptor> GetEnumerator() => _innerCollection.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); // 拦截Add方法 public void Add(ServiceDescriptor item) { ValidateRegistration(item); _innerCollection.Add(item); } // 拦截AddRange方法 public void AddRange(IEnumerable<ServiceDescriptor> items) { var violations = new List<InvalidOperationException>(); foreach (var item in items) { try { ValidateRegistration(item); } catch (InvalidOperationException ex) { if (_throwOnFirstViolation) throw; violations.Add(ex); } } if (violations.Any()) throw new AggregateException("发现多个重复服务注册", violations); _innerCollection.AddRange(items); } // 拦截Insert方法 public void Insert(int index, ServiceDescriptor item) { ValidateRegistration(item); _innerCollection.Insert(index, item); } // 拦截Set方法(索引器赋值) private void ValidateAndSet(int index, ServiceDescriptor value) { ValidateRegistration(value); _innerCollection[index] = value; } // 核心验证逻辑 private void ValidateRegistration(ServiceDescriptor newDescriptor) { // 检查是否存在完全匹配的注册 var existingMatches = _innerCollection.Where(s => s.ServiceType == newDescriptor.ServiceType && object.Equals(s.ServiceKey, newDescriptor.ServiceKey) && s.Lifetime == newDescriptor.Lifetime && AreImplementationsEquivalent(s, newDescriptor) ).ToList(); if (existingMatches.Any()) { throw new InvalidOperationException( $"服务{newDescriptor.ServiceType.FullName}(Key: {newDescriptor.ServiceKey}, 生命周期: {newDescriptor.Lifetime})已存在重复注册。" + $"现有实现:{GetImplementationDescription(existingMatches[0])},新实现:{GetImplementationDescription(newDescriptor)}"); } } // 判断两个服务的实现是否等效 private bool AreImplementationsEquivalent(ServiceDescriptor existing, ServiceDescriptor newDesc) { // 处理具体类型注册的情况 if (existing.ImplementationType != null && newDesc.ImplementationType != null) { return existing.ImplementationType == newDesc.ImplementationType; } // 处理实例注册的情况 if (existing.ImplementationInstance != null && newDesc.ImplementationInstance != null) { return existing.ImplementationInstance.GetType() == newDesc.ImplementationInstance.GetType(); } // 处理工厂注册的情况:这里可以根据需求调整,比如比较工厂委托的引用,或者允许自定义判定 if (existing.ImplementationFactory != null && newDesc.ImplementationFactory != null) { // 简单实现:比较委托引用;如果需要更灵活,可以允许注入自定义比较器 return existing.ImplementationFactory == newDesc.ImplementationFactory; } // 混合类型的情况(比如一个是类型注册,一个是工厂注册),判定为重复 return false; } // 获取服务实现的描述文本 private string GetImplementationDescription(ServiceDescriptor descriptor) { if (descriptor.ImplementationType != null) return descriptor.ImplementationType.FullName; if (descriptor.ImplementationInstance != null) return $"实例:{descriptor.ImplementationInstance.GetType().FullName}"; if (descriptor.ImplementationFactory != null) return "工厂委托"; return "未知类型"; } // 切换回普通模式 public IServiceCollection AsNormal() { return _innerCollection; } } // 扩展方法 public static class ServiceCollectionStrictModeExtensions { public static IStrictServiceCollection AsStrict(this IServiceCollection services, bool throwOnFirstViolation = true) { // 如果已经是严格模式,直接返回(避免重复包装) if (services is IStrictServiceCollection strictCollection) return strictCollection; return new StrictServiceCollection(services, throwOnFirstViolation); } }
2. 关键优化点
- 全方法拦截:覆盖了
Add、AddRange、Insert和索引器赋值等所有可能修改集合的操作 - 完善的重复判定:
- 包含服务类型、ServiceKey、生命周期三重匹配
- 支持类型注册、实例注册、工厂注册三种场景的等效判定
- 异常控制:通过
throwOnFirstViolation参数,支持立即抛出单个异常或汇总所有重复异常为AggregateException - 避免重复包装:在
AsStrict扩展方法中检查当前集合是否已经是严格模式,防止多层包装
3. 使用方式
services.AddX() .AsStrict(throwOnFirstViolation: false) // 汇总所有重复异常 .AddY() .AddZ() .AsNormal() .AddXXX();
可选增强
- 自定义判定逻辑:允许用户注入
Func<ServiceDescriptor, ServiceDescriptor, bool>来自定义重复判定规则 - 忽略特定服务:添加
IgnoreDuplicate<T>()或IgnoreDuplicate(Type serviceType)方法,允许特定服务跳过验证 - 日志记录:在抛出异常前记录重复注册的详细信息,便于排查问题
内容的提问来源于stack exchange,提问作者Ivan Petrov
相关产品推荐
相关产品推荐

