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

Avalonia应用EF Core并发调用DbContext报错求助

问题根源确认

你遇到的错误确实是同一DbContext实例被多线程并发访问导致的。Entity Framework Core的DbContext设计为非线程安全的,即使是Scoped注册,在同一个作用域内获取的是同一个实例,用Task.WhenAll并发执行两个查询时,多个线程同时操作这个实例,就会抛出InvalidOperationException。

解决方案1:为每个并发操作创建独立的DbContext作用域

既然Scoped服务在同一个作用域内是单例,可手动为每个查询创建新作用域,确保每个查询使用独立的DbContext:

// 在UserService中注入IServiceScopeFactory
private readonly IServiceScopeFactory _scopeFactory;

public UserService(IServiceScopeFactory scopeFactory)
{
    _scopeFactory = scopeFactory;
}

public async Task<(bool UsernameExists, bool EmailExists)> CheckDuplicatesAsync(string username, string email)
{
    // 为用户名检查创建独立作用域
    var usernameTask = Task.Run(async () =>
    {
        using var scope = _scopeFactory.CreateScope();
        var repo = scope.ServiceProvider.GetRequiredService<IUserRepository>();
        return await repo.IsUsernameExistsAsync(username);
    });

    // 为邮箱检查创建独立作用域
    var emailTask = Task.Run(async () =>
    {
        using var scope = _scopeFactory.CreateScope();
        var repo = scope.ServiceProvider.GetRequiredService<IUserRepository>();
        return await repo.IsEmailExistsAsync(email);
    });

    await Task.WhenAll(usernameTask, emailTask);
    return (usernameTask.Result, emailTask.Result);
}

解决方案2:合并查询为单次数据库调用

把两个存在性检查合并成一次数据库请求,既避免并发问题,又减少数据库往返次数提升性能:

// 在IUserRepository中添加合并查询方法
public async Task<(bool UsernameExists, bool EmailExists)> CheckUsernameAndEmailExistsAsync(string username, string email)
{
    var result = await _context.Users
        .Where(u => u.Username == username || u.Email == email)
        .Select(u => new 
        { 
            IsUsernameMatch = u.Username == username, 
            IsEmailMatch = u.Email == email 
        })
        .ToListAsync();

    bool usernameExists = result.Any(r => r.IsUsernameMatch);
    bool emailExists = result.Any(r => r.IsEmailMatch);
    return (usernameExists, emailExists);
}

之后直接调用该方法即可,无需并发执行。

解决方案3:使用DbContextFactory(EF Core 5+)

如果使用EF Core 5及以上版本,可注册IDbContextFactory<DatabaseContext>替代直接注册DbContext,每次获取的都是新的DbContext实例:

// 在Program.cs中注册服务
builder.Services.AddDbContextFactory<DatabaseContext>(options =>
    options.UseNpgsql("你的数据库连接字符串"));

在Repository中注入IDbContextFactory<DatabaseContext>,每次查询时创建新实例:

private readonly IDbContextFactory<DatabaseContext> _contextFactory;

public UserRepository(IDbContextFactory<DatabaseContext> contextFactory)
{
    _contextFactory = contextFactory;
}

public async Task<bool> IsUsernameExistsAsync(string username)
{
    using var context = _contextFactory.CreateDbContext();
    return await context.Users.AnyAsync(u => u.Username == username);
}

public async Task<bool> IsEmailExistsAsync(string email)
{
    using var context = _contextFactory.CreateDbContext();
    return await context.Users.AnyAsync(u => u.Email == email);
}

此时用Task.WhenAll并发调用两个方法时,每个方法都会使用独立的DbContext,不会触发线程安全问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 15:29:56