Передача сервисов с использованием внедрения зависимостей и шаблона фабрики в ASP.NET
Я использую ядро ASP.NET, я знаю, что такой механизм ведения журнала уже предоставляется платформой, но использую это, чтобы проиллюстрировать мою проблему.
Я использую шаблон Factory для создания класса Logger, так как я не знаю тип ведения журнала (потому что он хранится в БД).
Контракт ILogger
Log(string msg)
Затем LoggerFactory вернет ILogger после создания Logger на основе параметра, переданного из БД:
public class LoggerFactory
{
public static Contracts.ILogger BuildLogger(LogType type)
{
return GetLogger(type);
}
//other code is omitted, GetLogger will return an implementation of the related logger
Теперь, когда мне нужно использовать Logger, я должен сделать это следующим образом:
public class MyService
{
private ILogger _logger
public MyService()
{
_logger = LoggerFactory.BuildLogger("myType");
}
Но я намерен оставить свои классы без какой-либо реализации, мне нужно использовать Constructor DI в MyService и мне нужно внедрить все зависимости при запуске:
services.AddTransient<Contracts.ILogger, LoggerFactory.BuildLogger("param") > ();
Но это не сработает, нам нужно пройти конкретную реализацию. Как заставить это работать, используя DI, есть ли лучший подход для реализации этого?
1 ответ
В вашем подходе есть пара ошибок:
- Ваши услуги зависят от конкретного
LoggerFactory
тип, который является нарушением принципа внедрения зависимостей. - Выполнение этой дополнительной инициализации может сделать построение графа объектов ненадежным, в то время как конструкторы инъекций должны быть простыми.
- Это скрывает тот факт, что
ILogger
это реальная услуга, от которой зависит ваш потребитель. Это усложняет тестирование системы, усложняет обслуживание и усложняет анализ графов объектов. - Использование фабрики - это запах, так как фабрики вряд ли когда-либо будут правильным решением.
Вместо этого ваш сервис должен выглядеть следующим образом:
public class MyService
{
private ILogger _logger;
public MyService(ILogger logger)
{
_logger = logger;
}
}
Это значительно упрощает всех потребителей, которые зависят от ILogger
, Это также означает, что получение права ILogger
за MyService
становится ответственным за Корень Композиции, который является правильным местом для получения этих знаний.
Это, однако, означает, что вам может потребоваться перейти от встроенного DI-контейнера ASP.NET Core к более многофункциональной DI-библиотеке, поскольку встроенный контейнер не способен выполнять контекстную регистрацию для ILogger
при этом библиотека автоматически связывает и другие зависимости конструктора.
С контейнером ASP.NET Core DI вы можете подключать свои службы только с помощью делегата. Например:
services.AddTransient<MyService>(c => new MyService(
BuildLogger(typeof(MyService).Name),
c.GetRequiredService<ISomeOtherDependency>(),
c.GetRequiredService<IYetAnotherOne>());