You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 06:35:31