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

EF Core上下文多异步调用报错:Blazor Server库存查询问题排查

问题分析

出现a second operation was started on this context instance before a previous operation completed错误的核心原因:

  • Blazor Server中,Scoped生命周期的DbContext会绑定到用户的SignalR会话,同一用户短时间内触发的多次搜索请求会共享同一个DbContext实例,但EF Core的DbContext本身不是线程安全的,不允许同时执行多个操作。
  • 仓储代码中使用了同步的Count()方法,而非异步的CountAsync(),同步操作会阻塞上下文,进一步加剧并发冲突的概率。

解决方案

1. 替换同步EF操作为异步方法

将仓储中的同步计数改为异步版本,确保整个查询流程都是非阻塞的异步操作:

// 原代码
int count = defaultQuery.Count();

// 修改为异步版本
int count = await defaultQuery.CountAsync();

2. 实现输入防抖,减少并发请求

在Razor组件中添加防抖逻辑,等待用户停止输入一段时间后再执行搜索,避免短时间内多次触发请求:

private CancellationTokenSource? _debounceCts;

private async Task SearchUserInventoryProducts(int pageNumber)
{
    // 取消前一次未执行的防抖请求
    _debounceCts?.Cancel();
    _debounceCts = new CancellationTokenSource();

    try
    {
        // 等待300ms,用户停止输入后再执行搜索
        await Task.Delay(300, _debounceCts.Token);
        
        PaginationFilter.PageNumber = pageNumber;
        UserInventoryProductMappings = await _userInventoryProductMappingService
                                      .SearchByUserInventoryId(PaginationFilter, UserInventory.UserInventoryId);
    }
    catch (OperationCanceledException)
    {
        // 防抖请求被取消,直接忽略
    }
}

3. 使用DbContextFactory创建独立上下文实例

如果上述方法仍无法解决问题,可以改用IDbContextFactory为每个仓储操作创建独立的DbContext实例,彻底避免共享上下文的并发冲突:

步骤1:修改DbContext注册方式

// 原代码
builder.Services.AddDbContext<NimaDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("NimaDbConnection"));
});

// 修改为使用DbContextFactory
builder.Services.AddDbContextFactory<NimaDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("NimaDbConnection"));
});

步骤2:改造仓储层,使用工厂创建上下文

private readonly IDbContextFactory<NimaDbContext> _contextFactory;
private readonly ILogger<UserInventoryProductMappingRepository> _logger;

// 构造函数注入IDbContextFactory
public UserInventoryProductMappingRepository(IDbContextFactory<NimaDbContext> contextFactory, ILogger<UserInventoryProductMappingRepository> logger)
{
    _contextFactory = contextFactory;
    _logger = logger;
}

public async Task<PagedResponse<UserInventoryProductMapping>> SearchByUserInventoryId(PaginationQuery query, int userInventoryId)
{
    try
    {
        // 每次操作创建新的DbContext实例
        using var context = _contextFactory.CreateDbContext();
        var defaultQuery = context.Set<UserInventoryProductMapping>().Where(x => x.UserInventoryId == userInventoryId);

        // 后续查询逻辑保持不变,确保所有操作都用await
        if (query.SearchString != null)
        {
            defaultQuery = defaultQuery.Where(x => x.Product.NameLabel.LabelDescriptions.Any(x => x.Description.Contains(query.SearchString)));
        }

        if (query.SortBy != null && query.SortById != null)
        {
            switch (query.SortBy)
            {
                case "productCategory":
                    defaultQuery = defaultQuery.Where(x => x.Product.ProductCategoryId == query.SortById);
                    break;
                case "productSubCategory":
                    defaultQuery = defaultQuery.Where(x => x.Product.ProductSubCategoryId == query.SortById);
                    break;
            }
        }

        int count = await defaultQuery.CountAsync();
        return new PagedResponse<UserInventoryProductMapping>
        {
            Data = await defaultQuery
                    .Skip((query.PageNumber - 1) * query.PageSize)
                    .Take(query.PageSize)
                    .ToListAsync(),
            PageNumber = query.PageNumber,
            PageSize = query.PageSize,
            TotalPages = (int)Math.Ceiling(count / (double)query.PageSize)
        };
    }
    catch (Exception e)
    {
        _logger.LogError(e, e.Message);
        throw;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:48:23