如何将Simple Injector的RegisterDecorator迁移至.NET Core的IServiceCollection?
迁移Simple Injector装饰器模式到.NET Core DI的解决方案
问题根源分析
你当前的代码抛出循环依赖异常,是因为直接注册装饰器类型时,DI容器尝试解析装饰器的构造函数依赖IContentGetHandler时,发现当前注册的服务类型就是装饰器本身,形成了循环引用。正确的做法是让装饰器包裹之前注册的服务实例,而不是直接替换原始服务。
1. .NET Core DI中正确注册装饰器的方式
核心思路是使用工厂注册模式,让每个装饰器接收前一个服务(原始服务或上一个装饰器)的实例作为构造参数。
手动注册示例
// 1. 注册原始服务 services.AddSingleton<IContentGetHandler, AssembleContent>(); // 2. 注册单例装饰器:包裹原始服务 services.AddSingleton<IContentGetHandler>(sp => new RemoveUnsupportedFields(sp.GetRequiredService<IContentGetHandler>())); // 3. 注册瞬态装饰器:依次包裹之前的服务实例 services.AddTransient<IContentGetHandler>(sp => new LoadContentBaseFields(sp.GetRequiredService<IContentGetHandler>())); services.AddTransient<IContentGetHandler>(sp => new AddFieldsWithoutValues(sp.GetRequiredService<IContentGetHandler>())); services.AddTransient<IContentGetHandler>(sp => new SetCalcFieldErrors(sp.GetRequiredService<IContentGetHandler>())); services.AddTransient<IContentGetHandler>(sp => new HandleAssembleException(sp.GetRequiredService<IContentGetHandler>()));
封装通用注册方法
如果需要复用装饰器注册逻辑,可以改进你的RegisterDecorator方法,利用ActivatorUtilities自动处理装饰器的构造参数:
public void RegisterDecorator(Type serviceType, Type decoratorType, ServiceLifetime lifetime) { var originalDescriptor = _services.LastOrDefault(d => d.ServiceType == serviceType) ?? throw new InvalidOperationException($"Service of type {serviceType.Name} not registered."); _services.Add(new ServiceDescriptor( serviceType, sp => { // 解析前一个服务实例(原始服务或上一个装饰器) var originalService = sp.GetRequiredService(serviceType); // 创建装饰器实例,自动注入其他依赖+原始服务 return ActivatorUtilities.CreateInstance(sp, decoratorType, originalService); }, lifetime)); }
调用方式和你原来的逻辑一致:
services.AddSingleton<IContentGetHandler, AssembleContent>(); RegisterDecorator(typeof(IContentGetHandler), typeof(RemoveUnsupportedFields), ServiceLifetime.Singleton); RegisterDecorator(typeof(IContentGetHandler), typeof(LoadContentBaseFields), ServiceLifetime.Transient); // 后续装饰器依次注册
2. 服务移除与重新添加的优化方案
你的原始代码错误地移除了原始服务注册,导致装饰器失去依赖对象。实际上:
- 不需要移除任何服务注册:装饰器的工厂模式依赖的是前一个注册的服务实例,保留所有注册才能让DI容器正确解析嵌套关系。
- 如果确实需要替换某个服务(非装饰器场景),可以使用
services.Replace(ServiceDescriptor)方法,但这不适用于装饰器链的构建。
3. 确保装饰器的正确执行顺序
.NET Core DI中,装饰器的注册顺序直接决定执行顺序:
- 最后注册的装饰器会包裹在最外层,请求进入时最先执行;
- 最先注册的装饰器最靠近原始服务,最后执行。
你的Simple Injector注册顺序可以直接迁移到.NET Core,按相同顺序注册后,执行流程为:HandleAssembleException → SetCalcFieldErrors → AddFieldsWithoutValues → LoadContentBaseFields → RemoveUnsupportedFields → AssembleContent(请求进入方向),返回时则反向执行。
注意:如果装饰器生命周期不同(比如单例装饰器包裹瞬态服务),要确保生命周期兼容性——单例装饰器会持有其依赖的服务实例,导致该依赖的生命周期被提升为单例,需根据业务场景确认是否符合预期。
内容的提问来源于stack exchange,提问作者Ranjith Kumar
相关产品推荐
相关产品推荐

