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

.NET 7构建单例DI类时引用ApplicationDbContext报错求助

解决单例服务依赖Scoped DbContext的错误

问题根源

默认情况下,AddDbContext将ApplicationDbContext注册为Scoped生命周期服务,而单例服务的创建依赖根服务提供者,根容器不允许直接解析Scoped服务——这是DI框架的生命周期约束,避免Scoped服务被单例长期持有后,出现资源泄漏、多线程数据不一致等问题。

可行解决方案

方式一:仅在单例初始化时使用DbContext(临时作用域)

如果ApplicationSettings只在创建时需要读取DbContext的数据,之后不再使用DbContext,可以手动创建临时作用域来获取DbContext,使用后自动释放:

builder.Services.AddSingleton<IApplicationSettings, ApplicationSettings>(sp =>
{
    using var scope = sp.CreateScope();
    var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
    // 此处可读取DbContext数据用于初始化ApplicationSettings
    return new ApplicationSettings(new List<string> { "workers.maintenance", "shared" }, dbContext);
});

⚠️ 注意:如果ApplicationSettings后续还要调用DbContext的方法,这种方式不可行,因为作用域释放后DbContext会被销毁。

方式二:将DbContext注册为Singleton(不推荐)

可以强制指定DbContext的生命周期为Singleton,但这种做法严重违背DbContext的设计初衷,会引发多线程冲突、缓存污染、数据库连接异常等问题,仅在极端场景下考虑:

builder.Services.AddDbContext<ApplicationDbContext>(options => 
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), o => o.EnableRetryOnFailure()),
    ServiceLifetime.Singleton); // 强制设置为单例

方式三:重构为延迟获取DbContext(推荐)

如果ApplicationSettings需要在运行时多次使用DbContext,不要直接持有DbContext实例,而是注入工厂或服务提供者,在需要时创建/获取DbContext。

子方案3.1:使用IDbContextFactory

首先注册DbContext工厂:

builder.Services.AddDbContextFactory<ApplicationDbContext>(options => 
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), o => o.EnableRetryOnFailure()));

修改ApplicationSettings的构造函数,注入工厂并在使用时创建DbContext:

public class ApplicationSettings : IApplicationSettings
{
    private readonly IEnumerable<string> _names;
    private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;

    public ApplicationSettings(IEnumerable<string> names, IDbContextFactory<ApplicationDbContext> dbContextFactory)
    {
        _names = names;
        _dbContextFactory = dbContextFactory;
    }

    // 示例:需要操作数据库时创建DbContext
    public async Task<List<SomeEntity>> GetSomeDataAsync()
    {
        using var dbContext = _dbContextFactory.CreateDbContext();
        return await dbContext.SomeEntities.ToListAsync();
    }
}

注册单例时注入工厂:

builder.Services.AddSingleton<IApplicationSettings, ApplicationSettings>(sp =>
{
    var dbContextFactory = sp.GetRequiredService<IDbContextFactory<ApplicationDbContext>>();
    return new ApplicationSettings(new List<string> { "workers.maintenance", "shared" }, dbContextFactory);
});

子方案3.2:注入IServiceProvider

修改ApplicationSettings,注入服务提供者并在需要时创建作用域获取DbContext:

public class ApplicationSettings : IApplicationSettings
{
    private readonly IEnumerable<string> _names;
    private readonly IServiceProvider _serviceProvider;

    public ApplicationSettings(IEnumerable<string> names, IServiceProvider serviceProvider)
    {
        _names = names;
        _serviceProvider = serviceProvider;
    }

    public async Task<List<SomeEntity>> GetSomeDataAsync()
    {
        using var scope = _serviceProvider.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
        return await dbContext.SomeEntities.ToListAsync();
    }
}

注册单例时直接注入服务提供者:

builder.Services.AddSingleton<IApplicationSettings, ApplicationSettings>(sp =>
{
    return new ApplicationSettings(new List<string> { "workers.maintenance", "shared" }, sp);
});

内容的提问来源于stack exchange,提问作者Yohancef Chin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 18:12:52