Причина, по которой ваша переменная logger
равна null
в примере , состоит в том, что ILogger
фактически не зарегистрирован как служба. Если вы посмотрите на источник AddLogging
, вы увидите, что это только регистры ILogger<>
и ILoggerFactory
. Если вы когда-либо пытались принять ILogger
вместо ILogger<MyClass>
через. NET Core DI, вы столкнетесь со следующим исключением *:
System.InvalidOperationException: ' Невозможно разрешить службу для типа «Microsoft.Extensions.Logging.ILogger» при попытке активировать «Your.Service»
Исходя из этого, ваш код тестирования ошибочен, поскольку вы никогда бы не получил ILogger
во-первых. Чтобы убедиться, что ваш код на самом деле работает , измените свой тестовый код так, чтобы вместо него он получал ILogger<SomeClass>
. Результат вашей переменной будет отличным от нуля, и будет достигнута точка останова, установленная в конструкторе вашего провайдера:
// Get*Required*Service won't throw
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
Если вы хотите, чтобы имел возможность вводить ILogger
, вам нужно будет зарегистрировать его отдельно с именем категории по умолчанию **:
services.AddSingleton<ILoggerProvider, MyCustomProvider>(); // no need for lambda
services.AddSingleton<ILogger>(sp =>
sp.GetService<ILoggerFactory>().CreateLogger("Default")
);
Теперь оба будут работать и с использованием вашего настраиваемого поставщика:
var loggerA = serviceProvider.GetRequiredService<ILogger<Program>>();
var loggerB = serviceProvider.GetRequiredService<ILogger>();
У меня есть собственный настраиваемый поставщик / фабрика / регистратор, который я использую для внедрения xUnit's ITestOutputHelper
в настраиваемый регистратор, следуя тому же шаблону регистрации, что и вы, поэтому я знаю из личного опыта, что это работает. Но я также проверил, что ваш конкретный код c работает (имитирует мой собственный IDependency
) - приведенный выше код выполняется, и точки останова, установленные в конструкторе MyCustomProvider
, и регистрация службы срабатывают. Вдобавок, если я ввожу регистратор в класс, он тоже попадает (как и ожидалось).
Ваш комментарий «Каким-то образом это делается без указания какого-либо провайдера» дезинформирован, потому что у вас действительно есть зарегистрированный провайдер! Но даже в сценарии, когда все поставщики были очищены, а новые не добавлены, вы все равно получите ненулевой регистратор. Это потому, что LoggerFactory
просто перебирает каждого провайдера, чтобы создать вокруг них новую оболочку . Если провайдеров нет, то вы, по сути, получаете регистратор без операций.
* Это досадно, поскольку некоторые инструменты (например, R #) предлагают преобразовать параметр в базовый тип ILogger
, который затем нарушает DI!
** Требуется имя категории по умолчанию, поскольку нет аргумента generi c type для передачи в ILoggerFactory.CreateLogger
. Для generi c ILogger<T>
имя категории всегда является вариацией имени T
, но мы, очевидно, не получаем этого с не-generi c версией. Если бы мне пришлось угадать , вероятно, поэтому они не регистрируют реализацию ILogger
по умолчанию.