简体   繁体   中英

Getting IServiceProvider as a Dependency to get Optional Dependencies in ASP.NET Core

Is it a bad practice to get IServiceProvider injected to a service class, as a means to get optional dependencies in ASP.NET Core 2.0? Does this break Explicit Dependency Principal ?

I've a class which requires an Optional Service, EventBus. If the EventBus is registered, I want the service class to publish an event, if not simply ignore it.

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

I can't see how to create optional dependencies with the built in IoC Container of ASP.NET Core 2.0.

EDIT: Any suggestions how to implement optional dependencies in ASP.NET Core? Or any other strategy to get the same effect without the anti-pattern?

It would not be considered optional if it is required directly by the method in order for it to function correctly.

It should be explicitly injected as a dependency

public class SomeService {
    private readonly IEventBus eventBus;

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

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

        //...
    }
}

otherwise consider passing it explicitly to the method as an optional dependency

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

    //...
}

The Explicit Dependencies Principle states:

Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly .

emphasis mine

Injecting IServiceProvider is debated as an anti-pattern as it follows a service locator pattern.

There are some exceptions for example if the dependent class is being also used as a factory.

Injecting IServiceProvider is an implementation of the Service Locator anti-pattern . Prevent from doing this. Neither should dependencies be optional . This introduces complexity. Instead, use the Null Object pattern . Making the dependency required , simplifies the consumer and its test.

In other words, SomeService should look as follows:

public class SomeService {
   private readonly IEventBus _bus;

   public SomeService(IEventBus bus) {
      _bus = bus ?? throw new ArgumentNullException(nameof(bus));
   }

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

In your Composition Root you use a NullEventBus implementation in case no real implementation exists. This should be as easy as this:

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

Since this implementation does nothing, it can be injected into all consumers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM