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

Blazor EF Core子表编辑页跳转时DbContext线程冲突问题求助

Blazor DbContext线程冲突:主表跳转子表编辑页崩溃的解决方法

问题场景

Blazor项目中存在主表→子表→子子表的一对多关联结构,各表均关联查找表数据。主表编辑页、子子表编辑页功能正常,从首页跳转至子表编辑页(/editchild/{id})无报错,但从主表编辑页跳转至子表编辑页时,子表编辑页OnInitializedAsync加载查找表数据的代码会崩溃。

经排查,问题源于主表编辑页使用Radzen Grid实现主-子关联并开启行选择功能:点击跳转链接时,会同时触发行选择的仓储调用,导致DbContext因多线程并发访问引发冲突(DbContext本身线程不安全)。临时添加await Task.Delay(1000)可规避问题,但并非合理方案。

子表编辑页加载代码:

lookupTableAList = await ARepository.GetAllAsync();
lookupTableBList = await BRepository.GetAllAsync();
lookupTableCList = await CRepository.GetAllAsync();
lookupTableDList = await DRepository.GetAllAsync();
masterTableList = await MasterRepository.GetAllAsync();  // 获取主表记录用于下拉选择

泛型仓储实现:

public virtual async Task<List<T>> GetAllAsync()
{
    return await _db.Set<T>().ToListAsync();
}

主表仓储实现:

public async Task<IEnumerable<MasterEntity>> GetAllAsync()
{
    IEnumerable<MasterEntity> masterEntity = await _db.MasterEntity
        .Where(u => !u.IsDeleted)
        .OrderBy(o => o.Name)
        .ToListAsync();

    return masterEntity;
}

解决方案

1. 阻止跳转事件触发行选择(最直接有效)

Radzen Grid的行选择事件会通过事件冒泡响应点击行内元素,因此只需在跳转链接/按钮上阻止事件冒泡,即可避免两个事件同时触发:

方式1:给链接添加@onclick:stopPropagation="true"

<RadzenGridColumn>
    <Template Context="data">
        <a href="/editchild/@data.Id" @onclick:stopPropagation="true">编辑子表</a>
    </Template>
</RadzenGridColumn>

方式2:使用Radzen按钮+手动跳转并阻止冒泡

<RadzenGridColumn>
    <Template Context="data">
        <RadzenButton 
            Text="编辑" 
            Click="@(args => NavigationManager.NavigateTo($"/editchild/{data.Id}"))" 
            @onclick:stopPropagation="true" />
    </Template>
</RadzenGridColumn>

2. 确保DbContext与仓储的生命周期正确

DbContext是线程不安全的,必须确保每个组件实例使用独立的DbContext:

  • 注册DbContext为Scoped服务(Blazor Server默认推荐):
    services.AddDbContext<YourDbContext>(
        options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")),
        ServiceLifetime.Scoped);
    
  • 仓储类也需注册为Scoped服务,确保每个组件获取独立的仓储实例(及对应的DbContext):
    services.AddScoped<IARepository, ARepository>();
    services.AddScoped<IMasterRepository, MasterRepository>();
    // 其他仓储同理
    

3. 等待行选择操作完成后再跳转

若业务需要保留行选择与跳转的关联逻辑,可通过状态标记等待行选择操作完成:

private bool _isProcessingRowSelection = false;

// 行选择事件处理
async Task OnRowSelect(MasterEntity selectedItem)
{
    _isProcessingRowSelection = true;
    try
    {
        // 行选择时的异步仓储操作
        await SomeRepository.ProcessSelectionAsync(selectedItem.Id);
    }
    finally
    {
        _isProcessingRowSelection = false;
    }
}

// 跳转方法
async Task NavigateToChildEdit(int childId)
{
    // 等待行选择操作完成
    while (_isProcessingRowSelection)
    {
        await Task.Delay(50);
    }
    NavigationManager.NavigateTo($"/editchild/{childId}");
}

在页面中调用NavigateToChildEdit替代直接跳转:

<RadzenButton Text="编辑" Click="@(() => NavigateToChildEdit(data.Id))" />

4. 确保行选择事件的异步操作被正确等待

检查主表编辑页的行选择事件,确保所有异步仓储调用都使用await等待完成,避免后台未完成的操作占用DbContext:

// 错误示例:未等待异步操作,导致后台并发访问DbContext
void OnRowSelect(MasterEntity item)
{
    _ = SomeRepository.GetRelatedDataAsync(item.Id);
}

// 正确示例:等待异步操作完成
async Task OnRowSelect(MasterEntity item)
{
    await SomeRepository.GetRelatedDataAsync(item.Id);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 15:03:34