【问题标题】:Unable to resolve ILogger from Microsoft.Extensions.Logging无法从 Microsoft.Extensions.Logging 解析 ILogger
【发布时间】:2019-03-26 02:13:54
【问题描述】:

我已经像这样配置了我的控制台应用程序的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'

我已经尝试了here 的解决方案,但它对我不起作用。

编辑 根据下面 Yaakov 的评论和this Github comment,我可以通过这样做正确解决它

public MyClass(ILogger<MyClass> logger)
{
    _logger = logger;
}

我本来希望在最初的 BuildServiceProvider 中有这个,但看起来我每次想使用记录器(或创建我自己的 ILogger)时都必须重复这个。

【问题讨论】:

  • 那么您注册服务集合的部分在哪里?
  • @EmrahSüngü 我不认为我是单独做的。在..AddLogging(logging =&gt; logging.AddConsole()) 之后,我在该链中还有其他解析器,例如正确解析的.AddScoped&lt;IMyInterface, MyMethod&gt;()
  • 如果您尝试解析 ILogger&lt;MyClass&gt; 是否有效?
  • @yaakov 是的,确实如此。但是有没有其他方法可以注册,这样我就不必每次都做public MyClass(ILogger&lt;MyClass&gt; logger)
  • 我不知道。我相信它的设计方式是让 DI 还添加有关从何处记录数据的信息。

标签: c# .net dependency-injection .net-core ilogger


【解决方案1】:

ILogger 不再默认注册,但ILogger&lt;T&gt; 是。如果您仍想使用 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 现在将通过构造函数注入进行解析。

【讨论】:

  • 这被认为是不好的做法(从 ConfigureServices 构建您的服务提供者)。这样,您的所有单例服务都会被实例化两次。
  • @MathiasLykkegaardLorenzen 包括或指明首选实现会很有帮助。
  • 很确定你应该注册一个瞬态,在运行时解析ILogger&lt;ApplicationLog&gt;
  • 创建中间服务提供者有点阴暗,但这可能是避免它的解决方案:stackoverflow.com/a/70610564/646942
【解决方案2】:

我假设您正在使用 .net 核心 Web 应用程序的默认模板。
在您的 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>();
     }
}

编辑:程序的输出:

【讨论】:

  • 这是一个控制台应用程序,就像问题所说的那样。您在services.AddLogging 中显示的内容与我在问题中的内容有何不同?
  • 让我再问一次。var services = new ServiceCollection()你创建了一个新的服务集合对吗?你在哪里注册到你的应用程序?什么解决了依赖关系?谁创建了 MyClass 的新实例
  • 好吧,伙计,既然你拒绝了帮助 :) 除了建议阅读 DI 之外,我无能为力了
  • 抱歉,它应该是这样工作还是应该不工作
  • 这显示了它应该如何工作,正如您在示例代码中看到的那样,它可以工作并且 logger 是一个 ConsoleLogger。我不明白为什么我们不能互相理解:(
【解决方案3】:

在 .NET Core 中,ILogger&lt;T&gt; 会自动为您注册。由于ILogger&lt;T&gt; 继承自ILogger,您可以从IServiceProvider 请求ILogger&lt;T&gt; 的实例。

例如: services.AddSingleton&lt;ILogger&gt;(svc =&gt; svc.GetRequiredService&lt;ILogger&lt;MyClassType&gt;&gt;());

请注意,只要您有一个非泛型 ILogger 构造函数参数,这将返回一个 ILogger&lt;MyClassType&gt;,因此如果您需要多个参数,请考虑使用 AddSingleton 专门为该实例创建它(或瞬态/或作用域) implementationFactory 覆盖。

【讨论】:

    【解决方案4】:

    正如评论中所述,接受的答案存在BuildServiceProvider 不应在ConfigureServices 中使用的问题,因为它会创建每个单例(实际上是整个 DI 容器)的另一个副本。这是避免该问题的替代单线。在ConfigureServices 中添加一行

    services.AddSingleton<Microsoft.Extensions.Logging.ILogger>(provider => 
       provider.GetRequiredService<Microsoft.Extensions.Logging.ILogger<AnyClass>>());
    

    AnyClass 可以是任何类,如接受的答案中所述。这会将ILogger 的分辨率重定向到ILogger&lt;AnyClass&gt; 的分辨率。

    与公认的答案一样,这个问题的缺点是 Serilog 的 SourceContext 将被设置为 AnyClass。因此,如果您的 Serilog 配置包含一个包含 {SourceContext}outputTemplate,您的日志将显示 AnyClass 而不是包含日志记录语句的类。

    【讨论】:

    • 这是一个很好的方法。时尚干净。
    【解决方案5】:

    此答案不一定适用于 .net Core 或海报使用的任何依赖注入。我遇到了这个寻找一种在 asp.net 表单 Web 应用程序中使用 Autofac 修复此错误的方法。日志没有“默认注册”的答案导致我将其添加到我的注册中:

    builder.RegisterType<LoggerFactory>().As<ILoggerFactory>().SingleInstance();
    builder.RegisterGeneric(typeof(Logger<>)).As(typeof(ILogger<>)).SingleInstance();
    

    现在它对我有用。这是我第二次搜索这个答案并最终来到这里,我忘记了我第一次是如何修复它的。所以我想我会发布我的答案。

    【讨论】:

      【解决方案6】:

      对于 .NET Core 3.1,请务必包含

      Microsoft.Extensions.Logging

      通过调用打包和配置日志记录

      IServiceCollection services = new ServiceCollection();
      services.AddLogging();
      

      【讨论】:

      • 这是我一直在寻找的真正答案...
      【解决方案7】:

      根据@Barry 回答above 了解如何为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))
      

      【讨论】:

        【解决方案8】:

        我在尝试使用 ILogger 时使用的方法(因为它不再被注入),例如注入 ILoggerFactory,然后当您的代码需要特定的记录器时(比如说它的某个自定义工厂,您是 @ 987654323@ing up, LoggerFactory.CreateLogger("Logger Name") - 下面

        但对于你的情况,看起来你想要一个ILogger&lt;MyClass&gt;

        public class MyClass
        {
        
            private readonly ILoggerFactory _loggerFactory;
        
        
            public MyClass(ILoggerFactory loggerFactory)
            {
                _loggerFactory = _loggerFactory;
            }
            public void MyFunc()
            {
               new MyNewThatsASpecialCase(_loggerFactory.CreateLogger("MyNewThatsASpecialCase"));
            }
        }
        

        【讨论】:

        • 这对我有用,因为 LoggerFactory 是我请求服务时唯一可用的实例,而不是类记录器。
        【解决方案9】:

        这就是我让 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>();
            });
        
        【解决方案10】:

        对于使用 NLog 遇到此错误的任何人,我发现我在依赖注入中犯了以下错误:

                private readonly ILogger _logger;
        
                public ErrorController(ILogger ilogger)
                {
                    _logger = ilogger;
                }
        

        According to the documentation,在 .Net 5 中,您需要将 Controller 本身的类型添加到记录器以避免此问题。这样的依赖注入修复了错误:

                private readonly ILogger<ErrorController> _logger;
        
                public ErrorController(ILogger<ErrorController> ilogger)
                {
                    _logger = ilogger;
                }
        

        进行上述更改后,这解决了我的问题。不要在这里尝试任何解决方案!这只会让你陷入困境。

        希望这对将来的任何人都有帮助!

        【讨论】:

          【解决方案11】:

          接受的答案(由@sam-shiles 提供)朝着正确的方向发展。我不喜欢它需要构建一个中间服务提供者,并且可能也不需要注册单例实例。

          所以,我建议注册一个工厂,而不是使用ILoggerFactory。创建一个空类(如AnyClass 也不需要)。

          ...
          services.AddTransient(provider =>
          {
              var loggerFactory = provider.GetRequiredService<ILoggerFactory>();
              const string categoryName = "Any";
              return loggerFactory.CreateLogger(categoryName);
          });
          ...
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-03-08
            • 1970-01-01
            • 1970-01-01
            • 2016-08-19
            • 2020-11-02
            • 2015-03-31
            • 2018-11-19
            • 1970-01-01
            相关资源
            最近更新 更多