2

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?

1
  • 4
    That's called the service locator and yes, it's an anti-pattern that you should avoid Commented May 18, 2018 at 12:37

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.