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
相关产品推荐
相关产品推荐

