.NET 6:自定义日志提供程序中注入DbContext失败求助
解决无法将Scoped EFDataContext注入自定义日志提供程序的问题
问题原因
你的代码报错是因为根服务提供者(app.Services)无法解析作用域(Scoped)服务。EFDataContext是Scoped生命周期,而日志提供程序是单例(Singleton),直接从根容器获取Scoped服务会打破生命周期规则,同时根容器本身没有请求作用域,根本拿不到Scoped实例。而且你不能把DbContext改成单例,这会引发严重的并发问题。
解决方案
不要直接给日志提供程序注入EFDataContext,而是注入IServiceProvider,在需要写日志时临时创建作用域来获取DbContext,用完就释放,既符合生命周期规则,又避免并发问题。
步骤1:修改自定义日志提供类
让CustomLoggerProvider和CustomLogger依赖IServiceProvider,而不是直接依赖EFDataContext:
public class CustomLoggerProvider : ILoggerProvider { private readonly IServiceProvider _serviceProvider; public CustomLoggerProvider(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public ILogger CreateLogger(string categoryName) { return new CustomLogger(_serviceProvider); } public void Dispose() { // 按需添加资源清理逻辑 } } public class CustomLogger : ILogger { private readonly IServiceProvider _serviceProvider; public CustomLogger(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IDisposable BeginScope<TState>(TState state) => default!; public bool IsEnabled(LogLevel logLevel) { // 根据你的需求调整日志级别开关 return logLevel >= LogLevel.Information; } public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) { // 创建临时作用域,获取Scoped的EFDataContext using var scope = _serviceProvider.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<EFDataContext>(); // 执行你的日志写入逻辑 var logEntry = new LogEntry { Message = formatter(state, exception), LogLevel = logLevel.ToString(), CreatedAt = DateTime.UtcNow, Exception = exception?.ToString() }; dbContext.LogEntries.Add(logEntry); dbContext.SaveChanges(); } }
步骤2:修改Program.cs的注册代码
提前构建一个临时的服务提供者传入日志提供程序,不要用app.Services这个根容器:
builder.Services.AddScoped<EFDataContext>(); // 构建临时服务提供者传入日志提供程序 builder.Logging.AddProvider(new CustomLoggerProvider(builder.Services.BuildServiceProvider())); var app = builder.Build(); // 后续的app配置逻辑... app.UseHttpsRedirection(); // ...
关键说明
- 每次写日志时创建的作用域会自动释放
EFDataContext,避免了单例持有Scoped服务的问题。 - 这种方式既遵守了DI的生命周期规则,又能正常将日志写入数据库。
内容的提问来源于stack exchange,提问作者Jakub Puszyński
相关产品推荐
相关产品推荐

