首页 文章

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

提问于
浏览
1

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 回答

  • 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.
        }
    }
    

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

相关问题