Blazor中SQLite触发唯一约束异常后,后续插入均报错问题
问题原因与解决方案
问题根源
当SaveChangesAsync因唯一约束抛出异常时,导致冲突的实体仍然被EF Core的ChangeTracker跟踪着。后续调用AddObjectAsync插入新实体时,DbContext会尝试提交所有被跟踪的待插入实体(包括之前冲突的那个),哪怕新实体合法,也会因为旧冲突实体的存在再次触发唯一约束异常。重启程序后DbContext被重建,跟踪的实体清空,因此恢复正常。
修复方案
方案1:捕获异常后移除冲突实体
在catch块中将冲突实体从DbContext的跟踪中移除,避免后续操作重复提交它:
public async Task<MyObject> AddObjectAsync(MyObject myObject) { try { dbContext.MyObjects.Add(myObject); await dbContext.SaveChangesAsync(); } catch (Exception ex) { if (ex.InnerException is SqliteException sqliteEx && sqliteEx.SqliteErrorCode == 19) { // 解除冲突实体的跟踪状态 dbContext.Entry(myObject).State = EntityState.Detached; throw new SqliteException(sqliteEx.Message, sqliteEx.SqliteErrorCode); } else throw; } return myObject; }
方案2:使用短生命周期DbContext
如果当前DbContext是单例或长生命周期,改为每个操作/请求创建新的DbContext实例(比如依赖注入中配置为Scoped)。这样每次插入操作都用全新的DbContext,不存在旧实体残留问题:
// 示例:通过依赖注入获取Scoped生命周期的DbContext,或直接创建新实例 public async Task<MyObject> AddObjectAsync(MyObject myObject) { using var dbContext = new MyDbContext(); try { dbContext.MyObjects.Add(myObject); await dbContext.SaveChangesAsync(); } catch (Exception ex) { if (ex.InnerException is SqliteException sqliteEx) { throw new SqliteException(sqliteEx.Message, sqliteEx.SqliteErrorCode); } else throw; } return myObject; }
额外注意
- 不要复用同一个DbContext实例处理多次数据库操作,异常后ChangeTracker状态极易混乱。
- 可通过SQLite错误码(19对应唯一约束冲突)做精准处理,避免误清理其他异常场景的实体。
内容的提问来源于stack exchange,提问作者Christophe CGE
相关产品推荐
相关产品推荐

