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.