Наследование и DI
У меня есть простой класс Sender
public class Sender : ISender
{
private readonly FinMonitoringOptions options;
private readonly ILogger logger;
public Sender(IOptionsSnapshot<FinMonitoringOptions> _options, ILoggerFactory loggerFactory)
{
options = _options?.Value ?? throw new ArgumentNullException("_options");
logger = loggerFactory.CreateLogger<Sender>() ?? throw new ArgumentNullException("loggerFactory");
}
public async Task SendNotify(AlertCorp alert)
{
logger.LogInformation($"Sender.SendNotify IncidentId : {alert.IncidentId}");
}
}
теперь я хочу создать 2 класса-наследника EmailSender и ESBSender от этого же интерфейса ISender
public interface ISender
{
public Task SendNotify(AlertCorp alert);
}
я использую DI от Microsoft и развязываю зависимости в Startup
.AddTransient<ISender, Sender>()
Вопрос: как мне получить в классе Sender потомков EmailSender и EsbSender через DI и вызвать их методы SendNotify ?
Ответы (2 шт):
Вопрос: как мне получить в классе Sender потомков EmailSender и EsbSender через DI и вызвать их методы SendNotify ? - обычно это так не делается. Интерфейс вызывает свой метод. Который реализован в классе.
Согласно SOLID - а конкретно D - dependency inversion principle
Формулировка:
- Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.
- Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
В вашем случае
public class EmailSender : ISender
{
private readonly FinMonitoringOptions options;
private readonly ILogger logger;
public Sender(IOptionsSnapshot<FinMonitoringOptions> _options, ILoggerFactory loggerFactory)
{
options = _options?.Value ?? throw new ArgumentNullException("_options");
logger = loggerFactory.CreateLogger<Sender>() ?? throw new ArgumentNullException("loggerFactory");
}
public async Task SendNotify(AlertCorp alert)
{
logger.LogInformation($"Sender.SendNotify IncidentId : {alert.IncidentId}");
// send Email message
}
}
имея на руках только ISender. Вы должны вызвать метод sender.SendNotify(...) и не думать кто там на конце провода, который класс будет отсылать сообщение. В этом и есть основная идея интерфейсов.
А так можно в коде можно проверить что указатели совместимы и преобразовать
if (sender instanceof EmailSender) {
EmailSender emailSender = (EmailSender)sender;
...
// либо вариант. Кому как удобно
// EmailSender emailSender = sender AS EmailSender;
...
emailSender."call EmailSender method"()
}
Регистрируете оба типа как реализацию ISender:
services.AddTransient<ISender, EsbSender>();
services.AddTransient<ISender, EmailSender>();
В классе, который отправляет нотификации, добавляете зависимость от IEnumerable<ISender> senders.
public HomeController(ILogger<HomeController> logger, IEnumerable<ISender> senders)
{
_logger = logger;
_senders = senders;
}
В качестве значения senders от DI придет набор экземпляров всех зарегистрированных ISender.
Только не пытайтесь запихнуть управление отправкой через несколько ISender в базовый тип Sender. Это явно не его ответственность, заведите для этого отдельный класс, который знает про существование нескольких реализаций, и который будет выбирать, через какие именно отправлять.