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

Asp.Net Core HealthChecks并发Entity Framework DbContext错误求解决方案

问题根因

ASP.NET Core 默认会并行执行所有注册的健康检查,你项目中注入的业务服务、DbContext 均为 Scoped 生命周期,同一服务 Scope 内的 DbContext 实例唯一。两个并行执行的健康检查同时调用业务服务操作同一个 DbContext 实例,触发了 EF Core 禁止同一上下文并发操作的限制,所以抛出对应异常。

可行解决方案

以下方案均不需要复制业务逻辑,完全符合 DRY 原则:

  • 方案1:配置健康检查串行执行(改造成本最低)

仅需修改健康检查注册逻辑,设置最大并发执行数为1,所有健康检查按顺序逐个执行,完全不需要修改现有业务代码:

services.AddHealthChecks(options => 
{
    // 限制健康检查串行执行,避免并发操作同一个 Scoped DbContext
    options.MaximumConcurrentExecutions = 1;
})
.AddCheck<DuplicateTaskNamesHealthCheck>("Duplicate Task Names", HealthStatus.Unhealthy, tags: new[] { "org_project" })
.AddCheck<DuplicateProjectNamesHealthCheck>("Duplicate Project Names", HealthStatus.Unhealthy, tags: new[] { "org_project" });
  • 方案2:保留并行执行,每个健康检查单独创建服务 Scope

如果需要保留健康检查并行执行的能力,可以让每个健康检查单独创建独立的服务 Scope,获取独立的 DbContext 实例,避免实例冲突,仅需修改健康检查代码即可:

public class DuplicateProjectNamesHealthCheck : IHealthCheck
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly Settings _settings;

    public DuplicateProjectNamesHealthCheck(IServiceScopeFactory scopeFactory, IOptions<Settings> settings)
    {
        _scopeFactory = scopeFactory;
        _settings = settings.Value;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        // 创建独立服务Scope,获取专属的服务和DbContext实例
        using var scope = _scopeFactory.CreateScope();
        var projectService = scope.ServiceProvider.GetRequiredService<IACME6_PROJECT>();
        
        List<string> duplicateProjectNames = await projectService.GetDuplicateProjectNamesAsync();

        if (duplicateProjectNames.Count > 0)
        {
            if (_settings.AutoFixDuplicateProjectNames)
            {                    
                await projectService.FixDuplicateProjectNamesAsync();                    
            }
            else
            {
                string errorMessage = "存在重复的项目名称:";
                foreach (string projectName in duplicateProjectNames)
                {
                    errorMessage += projectName + ") |";
                }
                errorMessage += "如需自动修复,请在配置中将 AutoFixDuplicateProjectNames 设置为 true。";
                return new HealthCheckResult(status: context.Registration.FailureStatus, errorMessage);
            }
        }
        return HealthCheckResult.Healthy("正常");
    }
}

两个健康检查均按上述逻辑修改即可,各自的 Scope 完全独立,并行执行不会出现 DbContext 冲突。

  • 方案3:辅助优化业务查询逻辑

你现有业务方法中执行了两次数据库查询(AnyAsync 和 ToListAsync),可以合并为一次查询,减少 DbContext 操作次数,也能降低冲突概率:

public async Task<List<string>> GetDuplicateProjectNamesAsync()
{
    return await _context
        .PROJECT
        .AsNoTracking()
        .GroupBy(x => x.PROJECTNAME)
        .Where(x => x.Count() > 1)
        .Select(x => x.Key)
        .ToListAsync();
}

查询结果为空时会直接返回空列表,不需要额外判断是否存在数据,可减少一次数据库请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 21:51:02