Не удалось разрешить ILogger из Microsoft.Extensions.Logging
Я настроил консольное приложение Main
вот так
var services = new ServiceCollection()
.AddLogging(logging => logging.AddConsole())
.BuildServiceProvider();
А потом я пытаюсь использовать его в другом классе, так
private readonly ILogger _logger;
public MyClass(ILogger logger)
{
_logger = logger;
}
public void MyFunc()
{
_logger.Log(LogLevel.Error, "My Message");
}
System.InvalidOperationException: "Не удается разрешить службу для типа" Microsoft.Extensions.Logging.ILogger "
Я попробовал решения здесь, но это не сработало для меня.
Редактировать На основе комментария Яакова ниже и этого комментария на Github, я могу решить это правильно, сделав это
public MyClass(ILogger<MyClass> logger)
{
_logger = logger;
}
Я бы предпочел иметь это в начальной BuildServiceProvider
но похоже, что мне придется повторять это каждый раз, когда я хочу использовать регистратор (или создать свой собственный ILogger).
11 ответов
ILogger
больше не регистрируется по умолчанию, но ILogger<T>
является. Если вы все еще хотите использовать ILogger, вы можете зарегистрировать его вручную с помощью следующего (Startup.cs):
public void ConfigureServices(IServiceCollection services)
{
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<AnyClass>>();
services.AddSingleton(typeof(ILogger), logger);
...
}
Где AnyClass может быть чем-то общим, например:
public class ApplicationLogs
{
}
Так:
public void ConfigureServices(IServiceCollection services)
{
var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetService<ILogger<ApplicationLog>>();
services.AddSingleton(typeof(ILogger), logger);
...
}
ILogger теперь разрешается через инъекцию конструктора
Я предполагаю, что вы используете шаблон по умолчанию для основного веб-приложения.net.
В вашем Startup.cs у вас должен быть такой метод: ↓↓↓↓↓↓↓
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
//Do the service register here and extra stuff you want
services.AddLogging(config =>
{
config.AddDebug();
config.AddConsole();
//etc
});
}
Изменить: я написал простую программу для вас, чтобы показать, как она работает
public class MyClass
{
private readonly ILogger<MyClass> _logger;
public MyClass(ILogger<MyClass> logger)
{
_logger = logger;
}
public void MyFunc()
{
_logger.Log(LogLevel.Error, "My Message");
}
}
public class Program
{
public static void Main(string[] args)
{
var services = new ServiceCollection().AddLogging(logging => logging.AddConsole());
services.AddSingleton<MyClass>();//Singleton or transient?!
var s = services.BuildServiceProvider();
var myclass = s.GetService<MyClass>();
}
}
Для .NET Core 3.1 обязательно включите
Microsoft.Extensions.Logging
упаковать и настроить ведение журнала, позвонив
IServiceCollection services = new ServiceCollection();
services.AddLogging();
Для тех, кто получает эту ошибку с помощью NLog, я обнаружил, что делаю следующую ошибку в своей инъекции зависимостей:
private readonly ILogger _logger;
public ErrorController(ILogger ilogger)
{
_logger = ilogger;
}
Согласно документации , в .Net 5 нужно добавить в логгер тип самого Controller, чтобы избежать этой проблемы. Внедрение зависимостей как таковое исправило ошибку:
private readonly ILogger<ErrorController> _logger;
public ErrorController(ILogger<ErrorController> ilogger)
{
_logger = ilogger;
}
После внесения вышеуказанного изменения это устранило мою проблему. НЕ ПЫТАЙТЕСЬ ЛЮБЫМ РЕШЕНИЕМ ЗДЕСЬ! Это только отправит вас в кроличью нору.
Надеюсь, это поможет кому-нибудь в будущем!
В.NET Core ILogger<T>
автоматически регистрируется для вас. КакILogger<T>
наследуется от ILogger
, вы можете запросить экземпляр ILogger<T>
из IServiceProvider
.
Например:services.AddSingleton<ILogger>(svc => svc.GetRequiredService<ILogger<MyClassType>>());
Обратите внимание, что это вернет ILogger<MyClassType>
везде, где у вас нет общего ILogger
параметр конструктора, поэтому, если вам нужно более одного, рассмотрите возможность создания его специально для этого экземпляра с помощью AddSingleton
(или временный / или ограниченный) implementationFactory override.
Как отмечено в комментарии, у принятого ответа есть проблема, заключающаяся в том, что
BuildServiceProvider
не следует использовать в, поскольку он создает еще одну копию каждого синглтона (фактически, всего контейнера DI). Вот альтернативный однострочный вариант, который позволяет избежать этой проблемы. В
ConfigureServices
добавьте строку
services.AddSingleton<Microsoft.Extensions.Logging.ILogger>(provider =>
provider.GetRequiredService<Microsoft.Extensions.Logging.ILogger<AnyClass>>());
где может быть любой класс, как описано в принятом ответе. Это перенаправляет разрешение
ILogger
к тому из
ILogger<AnyClass>
.
Как и в случае с принятым ответом, у этого есть тот недостаток, что у Serilog
SourceContext
будет установлен на. Итак, если ваша конфигурация Serilog содержит
outputTemplate
это содержит
{SourceContext}
, в вашем журнале будет отображаться
AnyClass
вместо класса, содержащего оператор ведения журнала.
Этот ответ не обязательно для.net Core или какой-либо другой инъекции зависимостей, используемой плакатом. Я наткнулся на этот поиск способа исправить эту ошибку с помощью Autofac в веб-приложении форм asp.net. Ответ о том, что ведение журнала "не регистрируется по умолчанию", заставил меня добавить это в мои регистрации:
builder.RegisterType<LoggerFactory>().As<ILoggerFactory>().SingleInstance();
builder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>)).SingleInstance();
И теперь у меня работает. Это второй раз, когда я искал этот ответ и оказался здесь, я забыл, как я его исправил в первый раз. Итак, я подумал, что отправлю свой ответ.
Принятый ответ (предоставленный @sam-shiles) идет в правильном направлении. Мне не нравится, что для этого требуется создание промежуточного поставщика услуг, и регистрация экземпляра синглтона, возможно, также нежелательна.
Итак, я предлагаю вместо этого зарегистрировать фабрику, которая использует
ILoggerFactory
вместо. Создание пустого класса (например,
AnyClass
тоже не нужен).
...
services.AddTransient(provider =>
{
var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
const string categoryName = "Any";
return loggerFactory.CreateLogger(categoryName);
});
...
На основе @ Barry Franklin ответа выше выяснил , как зарегистрировать родовой поставщик журналу для
Microsoft.Extensions.DependencyInjection
:
var services = new ServiceCollection()
.AddLogging(logging => logging.AddConsole())
.BuildServiceProvider();
services.AddSingleton<ILoggerFactory, LoggerFactory>()
services.Add(ServiceDescriptor.Describe(typeof(ILogger<>),typeof(Logger<>),ServiceLifetime.Scoped))
Подход, который я использовал при попытке использовать ILogger
(Поскольку он больше не вводится), как вводить ILoggerFactory
, а затем, когда вашему коду требуется конкретный регистратор (скажем, это какая-то настраиваемая фабрика, вы new
встать, LoggerFactory.CreateLogger("Logger Name")
- Ниже
Но в вашем случае, похоже, вам нужен ILogger<MyClass>
public class MyClass
{
private readonly ILoggerFactory _loggerFactory;
public MyClass(ILoggerFactory loggerFactory)
{
_loggerFactory = _loggerFactory;
}
public void MyFunc()
{
new MyNewThatsASpecialCase(_loggerFactory.CreateLogger("MyNewThatsASpecialCase"));
}
}
Это то, что помогло мне заставить DI работать на ILogger.
Для .NET Core см. Https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0.
Конкретно:
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
})
Справка:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});