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

.NET8中DbContext并发操作异常排查及解决方案咨询

问题分析与解决方案

核心问题根源

  1. DbContext使用逻辑错误:配置了DbContextFactory却未正确利用,反而直接注入RepositoryContext到RepositoryWrapper,且Wrapper内部存在字段不匹配问题(构造函数赋值_repo1/_repo2/_repo3,但属性返回未赋值的_repo),导致上下文实例可能被意外共享。
  2. Blazor组件生命周期冲突:Blazor Server组件为Scoped生命周期,若RepositoryWrapper被组件缓存为私有字段,会导致同一上下文实例被多个异步操作并发调用,触发DbContext线程安全异常。
  3. 短生命周期原则未落地:DbContext本身非线程安全,必须保证每个独立操作(尤其是异步操作)使用专属实例,或同一实例上的操作严格串行执行。

优雅解决方案

1. 重构RepositoryWrapper,改用DbContextFactory创建上下文

放弃直接注入RepositoryContext,改为注入IDbContextFactory<RepositoryContext>,在需要数据库操作时动态创建上下文实例,用完自动释放。

保留Program.cs的Factory配置

builder.Services.AddDbContextFactory<RepositoryContext>(o => 
    o.UseSqlServer(builder.Configuration.GetConnectionString("Usdaw")), 
    ServiceLifetime.Transient);
builder.Services.AddTransient<IRepositoryWrapper, RepositoryWrapper>();

重构RepositoryWrapper代码

private readonly IDbContextFactory<RepositoryContext> _contextFactory;
private readonly IConfiguration _config;

// 移除多余的RepositoryContext参数,仅注入工厂和配置
public RepositoryWrapper(IConfiguration config, IDbContextFactory<RepositoryContext> contextFactory)
{
    _config = config;
    _contextFactory = contextFactory;
}

// 封装独立数据库操作,每个操作使用专属上下文
public async Task<ActivityLog?> GetActivityLogBySessionId(string sessionId)
{
    using var context = _contextFactory.CreateDbContext();
    return await context.ActivityLogs
        .Where(s => s.SessionID == Guid.Parse(sessionId))
        .FirstOrDefaultAsync();
}

// 多操作共享上下文时,确保串行执行
public async Task AddActivityLog(ActivityLog log)
{
    using var context = _contextFactory.CreateDbContext();
    context.ActivityLogs.Add(log);
    await context.SaveChangesAsync();
}

2. 修正RepositoryContext构造函数笔误

原代码中构造函数名称错误(ERepositoryContext应为RepositoryContext),修正后确保上下文能正确初始化:

public partial class RepositoryContext : DbContext
{
    public RepositoryContext()
    {
    }

    public RepositoryContext(DbContextOptions<RepositoryContext> options)
        : base(options)
    {
    }

    public virtual DbSet<ActivityLog> ActivityLogs { get; set; } = null!;
}

3. Blazor组件中避免缓存RepositoryWrapper

在Blazor Server组件中,不要将IRepositoryWrapper保存为组件私有字段,而是在每个方法中直接使用注入的实例:

@inject IRepositoryWrapper Wrapper

<button @onclick="LoadSessionLog">加载会话日志</button>

@code {
    private async Task LoadSessionLog()
    {
        var log = await Wrapper.GetActivityLogBySessionId(HttpContext.Session.Id);
        // 处理业务逻辑
    }
}

4. IIS应用池回收排查

应用池回收本身不会直接触发该异常,但回收时若存在未完成的异步数据库操作,可能导致上下文状态异常。确保所有DbContext实例通过using语句自动释放,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:50:17