Blazor中DbContext并发操作错误排查:未完成操作重复执行问题
扫描功能DbContext线程冲突问题排查
开发扫描功能时,按键监听器触发执行函数时出现以下错误:
在之前的操作完成前,对该上下文实例启动了第二个操作。这通常是不同线程同时使用同一DbContext实例导致的。
已经尝试用await处理异步进程,但问题仍未解决,线程冲突依旧,错误具体发生在GetByBarcode(String barcode)方法处。不确定是否遗漏了await的使用,或是问题本身更复杂,可能对await的理解有误。
相关代码片段:
前端触发代码
<div @onkeydown="@KeyDown" tabindex="0" @ref="scanner">
后端逻辑代码
public async Task KeyDown(KeyboardEventArgs e) { barcode += e.Key; await CheckBarcode(); } private async Task CheckBarcode() { testStuff = await stuffRepository.GetByBarcode(barcode); if(testStuff is null) { // 空逻辑 } else { await stuffRepository.Update(testStuff, loginID); await LoadStuff(); barcode = ""; } } public async Task<StuffDTO> GetByBarcode(string barcode) { var obj = await _db.StuffTable.FirstOrDefaultAsync(u => u.Barcode == barcode); if (obj != null) { return _mapper.Map<Stuff, StuffDTO>(obj); } return new StuffDTO(); }
问题原因与修复方案
核心原因
- DbContext非线程安全:如果
stuffRepository或DbContext是单例生命周期,多个线程会共用同一个实例,触发操作冲突。 - 按键触发频率过高:扫描枪按键触发速度极快,前一次
CheckBarcode还未执行完毕,下一次KeyDown又启动新操作,导致并发调用同一DbContext。
具体修复步骤
调整生命周期
确保stuffRepository和DbContext使用Scoped生命周期(ASP.NET Core默认),让每个操作都获取独立实例,避免跨线程复用。添加并发锁控制
在CheckBarcode方法上加锁,确保同一时间只有一个操作执行:private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private async Task CheckBarcode() { await _semaphore.WaitAsync(); try { testStuff = await stuffRepository.GetByBarcode(barcode); if(testStuff is null) { // 空逻辑 } else { await stuffRepository.Update(testStuff, loginID); await LoadStuff(); barcode = ""; } } finally { _semaphore.Release(); } }优化条码输入逻辑
扫描枪通常会在条码末尾发送Enter键,建议只在检测到Enter时才执行校验,减少不必要的并发:public async Task KeyDown(KeyboardEventArgs e) { if (e.Key == "Enter") { await CheckBarcode(); barcode = ""; } else { barcode += e.Key; } }验证异步操作正确性
确认所有DbContext相关操作都使用异步方法并正确await,避免同步阻塞导致的线程切换问题。
内容的提问来源于stack exchange,提问作者gbm0102
相关产品推荐
相关产品推荐

