Webapi 2 上带有 Serilog 的 DI ILogger 运行 Asp.Net Core 2

DI ILogger with Serilog on Webapi 2 running Asp.Net Core 2

我正在尝试使用 Asp.Net Core 2(与 MSSQLServer 集成)将 Serilog 集成到我的 WebApi 2。

我已经添加了 nuget 包,然后将以下内容添加到我的

public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

var log = new LoggerConfiguration()
                .WriteTo.MSSqlServer(DbGlobals.DevDatabase, "Logs")
                .CreateLogger();

loggerFactory.AddSerilog(log);

在我的 Program.cs 我有默认值;

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();

阅读 MSDN 文档 .CreateDefaultBuilder(args) 应该包括日志记录?

但是,当我将 ILogger DI 到我的控制器时;

[Route("api/[controller]")]
public class TraderController : Controller
{
    private TraderService _service;
    private readonly ILogger _logger;

    public TraderController(Func<IBemfeitoDataContext> context, ILogger logger)
    {
        _service = new TraderService(context, logger);
        _logger = logger;
    }

我收到以下错误;

InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger' while attempting to activate 'WebApi.Controllers.TraderContactController'.

我是否需要在 Startup.cs 中为 ILogger 物理设置单例。谁能告诉我我在这里缺少什么?正如我所假设的那样,因为日志记录是构建到核心管道中的,所以它知道如何解决这个问题?

您需要重构控制器以期待通用记录器 ILogger<T>,它派生自 ILogger

[Route("api/[controller]")]
public class TraderController : Controller {
    private TraderService _service;
    private readonly ILogger _logger;

    public TraderController(
            Func<IBemfeitoDataContext> context, 
            ILogger<TraderController> logger //<-- note the change here
    ) { 
        _service = new TraderService(context, logger);
        _logger = logger;
    }
}
当在 CreateDefaultBuilder 中调用 .AddLogging() 时,

ILogger<> 映射到 Logger<>ILogger 没有直接在服务集合中注册。

完成后,将控制器耦合到实现问题(即 TraderService)的当前设计选择引起了一些担忧。

假设基于当前代码的使用方式

public class TraderService : ITraderService {
    private readonly ILogger logger;

    public TraderService(Func<IBemfeitoDataContext> context, ILogger logger) {
        this.logger = logger;

        //...
    }

    //...code removed for brevity
}

该服务也应该固定为依赖 ILogger<T>

public class TraderService : ITraderService {
    private readonly ILogger logger;

    public TraderService(Func<IBemfeitoDataContext> context, ILogger<TraderService> logger) {
        this.logger = logger;
        //...
    }

    //...code removed for brevity
}

并且应该重构控制器以依赖于服务抽象而不是实现。

[Route("api/[controller]")]
public class TraderController : Controller {
    private readonly ITraderService service;
    private readonly ILogger logger;

    public TraderController(ITraderService service, ILogger<TraderController> logger) { 
        this.service = service;
        this.logger = logger;
    }

    //...
}

所有涉及的 类 都将在解析时显式注入它们的依赖项。

此答案假设 ITraderServiceTraderService 已在 DI 容器中注册。

ILoggerFactory 和通用 ILogger<T> 都被注入。这意味着您可以拥有这样的控制器:

   [Route("api/[controller]")]
   public class TraderController : Controller {
       private readonly ITraderService service;
       private readonly ILogger logger;
       public TraderController(ITraderService service, ILoggerFactory loggerFactory) 
       { 
           this.service = service;
           this.logger = loggerFactory.CreateLogger<TraderController>();
       }
       //...
   }