Ответ
Да, можно. Стандартный контейнер Microsoft.Extensions.DependencyInjection поддерживает эту возможность.
Существует несколько основных сценариев:
1. Регистрация и разрешение последней зарегистрированной реализации (по умолчанию)
При прямом запросе IService будет возвращена последняя зарегистрированная реализация.
// Регистрация в Program.cs
builder.Services.AddTransient<INotifier, EmailNotifier>();
builder.Services.AddTransient<INotifier, SmsNotifier>(); // Этот будет использован по умолчанию
// Внедрение и использование (получим SmsNotifier)
public class OrderService
{
private readonly INotifier _notifier;
public OrderService(INotifier notifier) // Внедрится SmsNotifier
{
_notifier = notifier;
}
}
2. Разрешение ВСЕХ зарегистрированных реализаций
Для реализации паттерна «Стратегия» или цепочки ответственности можно запросить IEnumerable<T>.
// Регистрация нескольких валидаторов
builder.Services.AddTransient<IOrderValidator, StockValidator>();
builder.Services.AddTransient<IOrderValidator, PaymentValidator>();
builder.Services.AddTransient<IOrderValidator, FraudValidator>();
// Внедрение коллекции всех валидаторов
public class OrderProcessor
{
private readonly IEnumerable<IOrderValidator> _validators;
public OrderProcessor(IEnumerable<IOrderValidator> validators)
{
_validators = validators; // Получим массив из 3-х валидаторов
}
public bool Validate(Order order)
{
// Последовательно выполняем все валидации
return _validators.All(v => v.Validate(order));
}
}
3. Именованное или типизированное разрешение (требует кастомного подхода)
Стандартный контейнер не поддерживает именованные регистрации «из коробки». Этого можно достичь, используя фабрику:
// Регистрация с ключом
builder.Services.AddTransient<EmailNotifier>();
builder.Services.AddTransient<SmsNotifier>();
builder.Services.AddTransient<Func<string, INotifier>>(serviceProvider => key =>
{
return key switch
{
"email" => serviceProvider.GetRequiredService<EmailNotifier>(),
"sms" => serviceProvider.GetRequiredService<SmsNotifier>(),
_ => throw new KeyNotFoundException()
};
});
// Использование
public class NotificationService
{
private readonly Func<string, INotifier> _notifierFactory;
public void Notify(string channel)
{
var notifier = _notifierFactory(channel); // Получаем нужную реализацию по ключу
notifier.Send("Hello");
}
}
Для сложных сценариев можно рассмотреть сторонние контейнеры (Autofac, Lamar), которые имеют встроенную поддержку именованных регистраций.