将IServiceProvider作为依赖项来获取ASP.NET Core中的可选依赖项

时间:2018-05-18 12:34:51

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

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!");
      }
   }
}

我无法看到如何使用内置的IoC Container of ASP.NET Core 2.0创建可选的依赖项。

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

2 个答案:

答案 0 :(得分:2)

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

它应该明确地作为依赖注入

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被视为反模式,因为它遵循服务定位器模式。

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

答案 1 :(得分:1)

注入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.
    }
}

由于此实现不执行任何操作,因此可以将其注入所有使用者。