You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:03:16