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

.NET DI运行时解析实现方案咨询 避免违反依赖倒置原则

推荐实现方案:上下文感知装饰器+作用域上下文

这个方案完全符合你偏好的装饰器模式,不违反依赖倒置原则,也无需直接注入IServiceProvider,对现有业务代码侵入极低。

实现步骤

  • 第一步:定义作用域级别的操作上下文
    新增一个仅在当前请求/操作作用域内生效的上下文类,用来标记当前链路是否需要开启日志:
public class RetailerOperationContext
{
    public bool EnableLogging { get; set; }
    // 可扩展存储用户信息等其他链路上下文
}

DI注册时指定为作用域生命周期:

services.AddScoped<RetailerOperationContext>();
  • 第二步:为需要日志的服务实现通用装饰器
    以IInventoryManager为例,其余服务的装饰器逻辑完全一致:
public class LoggingInventoryManagerDecorator : IInventoryManager
{
    private readonly IInventoryManager _inner;
    private readonly ILogger<LoggingInventoryManagerDecorator> _logger;
    private readonly RetailerOperationContext _context;

    public LoggingInventoryManagerDecorator(IInventoryManager inner, 
        ILogger<LoggingInventoryManagerDecorator> logger,
        RetailerOperationContext context)
    {
        _inner = inner;
        _logger = logger;
        _context = context;
    }

    public async Task Execute(/* 原有方法参数 */)
    {
        if (_context.EnableLogging)
        {
            _logger.LogInformation("开始执行InventoryManager操作,参数:{Params}", /* 参数序列化结果 */);
        }
        
        await _inner.Execute(/* 原有参数透传 */);
        
        if (_context.EnableLogging)
        {
            _logger.LogInformation("InventoryManager操作执行完成");
        }
    }
}
  • 第三步:DI注册装饰器
    推荐使用Scrutor库简化装饰器注册,只需在原有服务注册后新增一行装饰器配置即可:
// 原有业务服务注册逻辑
services.AddScoped<IInventoryManager, InventoryManager>();
services.AddScoped<IProductRepository, ProductRepository>();
services.AddScoped<IOrderService, OrderService>();

// 批量注册对应装饰器
services.Decorate<IInventoryManager, LoggingInventoryManagerDecorator>();
services.Decorate<IProductRepository, LoggingProductRepositoryDecorator>();
services.Decorate<IOrderService, LoggingOrderServiceDecorator>();

如果不想引入第三方库,也可以用原生DI实现:

services.AddScoped<InventoryManager>();
services.AddScoped<IInventoryManager>(sp => 
    new LoggingInventoryManagerDecorator(
        sp.GetRequiredService<InventoryManager>(),
        sp.GetRequiredService<ILogger<LoggingInventoryManagerDecorator>>(),
        sp.GetRequiredService<RetailerOperationContext>()
    ));
  • 第四步:在入口服务控制日志开关
    修改IRetailerService的实现,在对应重载中设置上下文标记:
public class RetailerService : IRetailerService
{
    private readonly IOrderService _orderService;
    private readonly RetailerOperationContext _context;

    public RetailerService(IOrderService orderService, RetailerOperationContext context)
    {
        _orderService = orderService;
        _context = context;
    }
    
    async Task IRetailerService.Execute(Guid id)
    {
        _context.EnableLogging = false;
        await _orderService.Get(id);
    }
    async Task IRetailerService.Execute(Guid id, User user)
    {
        _context.EnableLogging = true;
        await _orderService.Get(id, user);
    }
}

方案优势

  • 完全符合SOLID原则,依赖注入逻辑清晰,不存在服务定位器反模式的问题
  • 日志逻辑和业务逻辑完全解耦,现有业务服务的实现无需做任何修改
  • 扩展成本低,后续需要新增链路控制逻辑只需在上下文类中新增属性即可
  • 支持灵活调整日志范围,不需要记录日志的服务无需实现装饰器

扩展场景参考

如果你的场景中需要控制的链路逻辑非常复杂,也可以考虑将不同参数的Execute方法封装为不同的请求对象,通过中介者模式的管道行为实现全局的链路控制,这个方案适合后续业务持续迭代、链路规则越来越复杂的场景。


内容的提问来源于stack exchange,提问作者laventnc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 20:45:01