是的,但是使用 ILogger 中的 DbContext 并不是最好的主意。记录器和日志接收器是独立的实体。
在单例中使用作用域服务并不罕见。 Background services 就是这种情况,它被注册为单例。 Consuming a scoped service in a background task 部分解释了使用范围服务。
要使用作用域(或瞬态)服务,单例需要访问IServiceProvider。这通常作为构造函数依赖项传递。
public class MyHostedService : BackgroundService
{
private readonly ILogger<MyHostedService> _logger;
public MyHostedService(IServiceProvider services,
ILogger<MyHostedService> logger)
{
Services = services;
_logger = logger;
}
}
该类可用于使用IServiceProvider.GetService 或GetRequiredService 检索瞬态实例:
var service = Services.GetRequiredService<MyTransientService>();
要使用作用域服务,单例需要显式创建作用域并从该作用域检索服务。当作用域被释放时,所有作用域实例也将被释放
using (var scope = Services.CreateScope())
{
var context =
scope.ServiceProvider
.GetRequiredService<MyDbContext>();
...
context.SaveChanges();
}
记录器和数据库
ILogger 不需要依赖于 DbContext。日志库使用单独的接口来发布和存储日志条目。 ILogger 实例用于发布日志条目,而不是存储它。存储日志事件是日志接收器或日志提供者的工作 - 不同的日志库为此使用不同的名称。
.NET Core 有意提供很少的日志接收器。有像 Serilog 这样非常好的日志库,.NET Core 团队无意复制他们的工作。他们确实为 Azure Application Insights 等 Microsoft 特定服务提供了一些接收器实现。
可以编写一个写入数据库的自定义日志接收器,但最好使用已经提供此功能并负责缓冲、异步操作等的现有库。写入数据库总是比写入数据库更昂贵本地文件,因此数据库接收器需要缓冲消息以减少对应用程序的影响。
使用 Serilog.AspNetCore 等现有库之一很容易集成,例如 Serilog。
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
....
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog() // <-- Add this line
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
该库反过来提供像 Serilog.Sinks.SqlServer 这样的数据库接收器。
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.MSSqlServer(
connectionString: "Server=localhost;Database=LogDb;Integrated Security=SSPI;",
sinkOptions: new MSSqlServerSinkOptions { TableName = "LogEvents" })
.CreateLogger();
SQL Server 接收器将创建日志表并开始向其中写入日志条目。 UseSerilog() 调用会将所有 ILogger 调用重定向到 Serilog,并最终重定向到数据库表。