首页 > 解决方案 > .NET DI 与运行时实现解析器

问题描述

我有一个涉及 DI 的奇怪案例,特别是在运行时从同一服务中解决实现。我知道我可以注入服务提供者,但这似乎违反了依赖倒置原则

此外,如果这最终更像是一个建筑/设计问题,我们深表歉意;我最近从 .NET Framework 开发转向,但仍然熟悉 DI 的局限性。请注意,出于显而易见的原因,我已经简化并更改了业务环境,因此请记住,层次结构/结构是重要部分……对于这个问题,我决定使用在线零售商的经典示例。

项目概述/示例:

核心库(.NET 类库)
  - IRetailerService:客户端应用程序使用的公共服务
    └ IOrderService:注入到外观/聚合服务中 ^ 
      ├ IInventoryManager:注入到外观/聚合服务以及其他组件中的内部组件
      ├ IProductRespository
      └ IPriceEstimator

综合/立面服务

public class RetailerService : IRetailerService
{
    private readonly IOrderService _orderService;

    public OrderService( IOrderService orderService, ... ) { //... set injected components }
    
    async Task IRetailerService.Execute( Guid id )
    {
        await _orderService.Get( id );
    }
    async Task IRetailerService.Execute( Guid id, User user )
    {
        await _orderService.Get( id, user );
    }
}
internal class OrderService : IOrderService
{
    public OrderService( IInventoryManager inventoryManager, IProductRespository productRepo, ... ) { }

    async Task<object> IOrderService.Get( Guid id )
    {
        //... do stuff with the injected components
        await _inventoryManager.Execute( ...args );
        await _productRepo.Execute( ...args );
    }
    async Task<object> IOrderService.Get( Guid id, User user ) { }
}

问题:

假设我想记录IOrderService.Get( Guid id, User user ),但仅当提供了此覆盖时User-这也包括在注入的组件(InventoryManager、IProductRepository 等)内记录

我目前能看到的唯一解决方案是:

  1. 向此层次结构添加一个附加层并使用具有作用域生命周期的命名注册来确定是否传递了 null vs logging 实现。
  2. 将服务提供者注入到面向公众的服务IRetailerService中,并以某种方式传递正确的实现。

我认为我理想的解决方案是某种类型的装饰器/中间件来控制它......我只给出了核心库代码;但是解决方案中还有一个 WebApi 项目引用了这个库。任何想法/指导将不胜感激。

标签: c#.netdependency-injectioninversion-of-control.net-5

解决方案


您可以IOrderService.Get在运行时解决方法中的依赖关系,以便每个方法都有自己的依赖关系。然而,这并不能完全解决您的问题。嵌套依赖项IInventoryManager inventoryManager, IProductRespository productRepo, ...也应该能够启用日志记录。

因此,您可以使用:

internal class OrderService : IOrderService
{
    public OrderService( IServiceProvider serviceProvider) { }

    async Task<object> IOrderService.Get( Guid id )
    {
        var inventoryManager = (IInventoryManager)serviceProvider.GetService(typeof(IInventoryManager));
        inventoryManager.Logging = false;
        var productRepo = (IProductRespository)serviceProvider.GetService(typeof(IProductRespository));
        productRepo.Logging = false;
        //... do stuff with the injected components
        await inventoryManager.Execute( ...args );
        await productRepo.Execute( ...args );
    }
    async Task<object> IOrderService.Get( Guid id, User user ) {
        var inventoryManager = (IInventoryManager)serviceProvider.GetService(typeof(IInventoryManager));
        inventoryManager.Logging = false;
        var productRepo = (IProductRespository)serviceProvider.GetService(typeof(IProductRespository));
        productRepo.Logging = true;
        //... do stuff with the injected components
        await inventoryManager.Execute( ...args );
        await productRepo.Execute( ...args );
    }
}

您还可以为 Factory / Builder 提供一个参数以启用日志记录。但无论如何,因为您希望从同一个根类开始的嵌套类中有不同的行为,这可能很复杂。

另一种选择是提供 2 个 IOrderService 实现,一个包含日志记录,另一个不包含。但我不确定这是否会对您有所帮助,因为您可能有充分的理由为该方法提供重载,而不是将它们拆分为单独的服务。这并不能解决嵌套注入的问题。

最后一个选项可能是使用单例 LoggingOptions 类。每个依赖项都依赖于此类,并且因为这是一个单例,所以每次输入重载时都将其设置为 true,因此所有类都会被告知您要记录的意图。然而,这在很大程度上取决于您的架构。如果这两个方法可能几乎同时被调用,这可能会破坏嵌套依赖项的日志记录行为或随时中断日志记录。

看看这个问题,这可能会有所帮助。通过考虑这个问题,您可以为每个依赖项(包括嵌套的依赖项)提供一个工厂,该工厂将在每次调用重载方法时设置日志记录行为。


推荐阅读