.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
相关产品推荐
相关产品推荐

