【问题标题】:Getting IServiceProvider as a Dependency to get Optional Dependencies in ASP.NET Core获取 IServiceProvider 作为依赖项以获取 ASP.NET Core 中的可选依赖项
【发布时间】:2018-05-18 12:34:51
【问题描述】:

IServiceProvider 注入到服务类中,作为在 ASP.NET Core 2.0 中获取可选依赖项的一种方法,这是一种不好的做法吗?这会破坏Explicit Dependency Principal吗?

我有一个需要可选服务 EventBus 的类。如果 EventBus 已注册,我希望服务类发布一个事件,如果不是简单地忽略它。

public class SomeService {
   private readonly IServiceProvider _serviceProvider;

   public SomeService(IServiceProvider serviceProvider) {
      _serviceProvider = serviceProvider;
   }

   public SomeAction() {
      var eventBus = _serviceProvider.GetService(typeof(IEventBus)) as IEventBus;
      if (eventBus != null) {
           eventBus.publish("SomeAction Happened!");
      }
   }
}

我看不到如何使用 ASP.NET Core 2.0 的内置 IoC 容器创建可选依赖项。

编辑:关于如何在 ASP.NET Core 中实现可选依赖项的任何建议?或者在没有反模式的情况下获得相同效果的任何其他策略?

【问题讨论】:

  • 这就是所谓的服务定位器,是的,这是一个你应该避免的反模式

标签: c# asp.net-core dependency-injection anti-patterns


【解决方案1】:

如果方法直接需要它以使其正常运行,则它不会被视为可选。

它应该作为依赖项显式注入

public class SomeService {
    private readonly IEventBus eventBus;

    public SomeService(IEventBus eventBus) {
        this.eventBus = eventBus;
    }

    public SomeAction() {
        if (eventBus != null) {
            eventBus.publish("SomeAction Happened!");
        }

        //...
    }
}

否则考虑将其作为可选依赖项显式传递给方法

public SomeAction(IEventBus eventBus = null) {
    if (eventBus != null) {
        eventBus.publish("SomeAction Happened!");
    }

    //...
}

显式依赖原则指出:

方法和类应该明确要求(通常通过方法参数或 构造函数参数)按顺序需要的任何协作对象 正常运行

强调我的

注入 IServiceProvider 被认为是一种反模式,因为它遵循服务定位器模式。

有一些例外,例如,如果依赖类也被用作工厂。

【讨论】:

    【解决方案2】:

    注入IServiceProviderService Locator anti-pattern 的实现。防止这样做。依赖项也不应该是optional。这引入了复杂性。相反,请使用Null Object pattern。使依赖项必需,简化了消费者及其测试。

    换句话说,SomeService 应该如下所示:

    public class SomeService {
       private readonly IEventBus _bus;
    
       public SomeService(IEventBus bus) {
          _bus = bus ?? throw new ArgumentNullException(nameof(bus));
       }
    
       public SomeAction() {
           eventBus.publish("SomeAction Happened!");
       }
    }
    

    在您的Composition Root 中,您使用NullEventBus 实现,以防不存在真正的实现。这应该像这样简单:

    public class NullEventBus : IEventBus
    {
        public void publish(string message) {
            // do nothing.
        }
    }
    

    由于这个实现什么都不做,它可以被注入到所有的消费者中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-24
      • 1970-01-01
      • 1970-01-01
      • 2012-03-30
      • 1970-01-01
      • 2016-10-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多