Blazor Server项目保存数据时偶现“当前回路未处理异常”问题排查
我正在使用VS 2022开发Blazor Server项目,调用以下updateObject方法保存数据时偶发未处理异常提示,错误核心信息为:System.InvalidOperationException: An exception was thrown while activating Microsoft.AspNetCore.Components.Server.Circuits.CircuitAccessor.
该错误为偶发,调试时无异常提示,且每次操作的数据均已正常保存,仅在操作完成后出现上述错误提示。
相关代码
数据层方法
async public static Task<bool> updateObject(Firecall obj) { Firecall r; try { using (var context = new sptContext()) { r = context.Firecalls.Where(c => c.Mguid == obj.Mguid).FirstOrDefault(); bool новое = (r == null); if (новое) { r = new Firecall(); } r.comment = obj.comment; if (новое) await context.Firecalls.AddAsync(r); if (busy) return false; try { busy = true; await context.SaveChangesAsync(); } catch (Exception) { return false; } finally { busy = false; } } return true; } catch (Exception) { return false; } }
Razor视图调用代码
async private void SaveChanges() { bool rez = await firecallRepository.updateObject(_currentFireCall); }
1. 替换async void为async Task
Blazor Server中组件的异步方法不能用async void,这种写法会导致异步操作的异常无法被Blazor框架捕获,进而触发全局未处理异常提示。修改Razor中的调用方法:
async private Task SaveChanges() { bool rez = await firecallRepository.updateObject(_currentFireCall); }
UI触发时直接绑定方法即可,比如按钮点击用@onclick="SaveChanges",确保框架能正确跟踪异步操作的生命周期。
2. 修复静态busy变量的线程安全问题
updateObject是静态方法,且使用了全局共享的静态busy字段,在Blazor Server多用户/并发场景下会互相干扰,导致逻辑混乱和异常。解决方式:
- 若要防止同一用户重复提交,将
busy改为组件级别的实例变量(比如组件内的private bool _isBusy),在调用保存方法前先判断状态。 - 若要实现全局并发控制,使用线程安全的
SemaphoreSlim替代静态布尔值:
// 替换静态busy为线程安全的信号量 private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); async public static Task<bool> updateObject(Firecall obj) { Firecall r; try { using (var context = new sptContext()) { r = context.Firecalls.Where(c => c.Mguid == obj.Mguid).FirstOrDefault(); bool новое = (r == null); if (новое) { r = new Firecall(); } r.comment = obj.comment; if (новое) await context.Firecalls.AddAsync(r); // 等待信号量,避免并发操作冲突 await _semaphore.WaitAsync(); try { await context.SaveChangesAsync(); } catch (Exception) { return false; } finally { _semaphore.Release(); } } return true; } catch (Exception) { return false; } }
3. 取消仓库方法的静态特性,改用依赖注入
静态类/方法在Blazor Server中会带来全局状态污染问题,建议将仓库改为实例类,通过依赖注入(Scoped或Transient)注入到组件中,同时添加日志记录便于排查偶发异常:
// 仓库类改为实例类,注入ILogger public class FirecallRepository { private readonly ILogger<FirecallRepository> _logger; private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); public FirecallRepository(ILogger<FirecallRepository> logger) { _logger = logger; } async public Task<bool> updateObject(Firecall obj) { Firecall r; try { using (var context = new sptContext()) { r = context.Firecalls.Where(c => c.Mguid == obj.Mguid).FirstOrDefault(); bool новое = (r == null); if (новое) { r = new Firecall(); } r.comment = obj.comment; if (новое) await context.Firecalls.AddAsync(r); await _semaphore.WaitAsync(); try { await context.SaveChangesAsync(); } catch (Exception ex) { _logger.LogError(ex, "保存Firecall数据失败"); return false; } finally { _semaphore.Release(); } } return true; } catch (Exception ex) { _logger.LogError(ex, "处理Firecall数据时发生异常"); return false; } } }
在Program.cs中注册仓库:
builder.Services.AddScoped<FirecallRepository>();
4. 验证DbContext的注册方式
确保sptContext在Blazor Server中注册为Scoped(默认推荐方式),避免并发操作时的上下文冲突:
builder.Services.AddDbContext<sptContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString")));
内容的提问来源于stack exchange,提问作者А.Терентьев

