EF Core事务回滚失效:数据仍被提交至MySQL数据库
问题描述
原事务代码如下,当//业务逻辑部分出错时,明明执行了transaction.Rollback(),但数据仍被提交到MySQL数据库:
using (var transaction = _context.Database.BeginTransaction()) { try { // 业务逻辑(包含Remove、Add、Update、SaveChanges操作) _context.SaveChanges(); transaction.Commit(); } catch { transaction.Rollback(); } }
后续补充代码发现,问题出在外部的SaveChanges提交了已回滚的变更:
public void AddUser() { using (var transaction = _context.Database.BeginTransaction()) { try { // 业务逻辑 _context.SaveChanges(); transaction.Commit(); } catch { transaction.Rollback(); throw; } } } public void Init(){ try { AddUser(); } catch(Exception e) { _context.Logs.Add(new Log(e.Message)); _context.SaveChanges(); // <-------- 这里提交了AddUser()中已回滚的变更 } }
问题根源
EF Core的DbContext会持续追踪所有实体变更,哪怕事务回滚,这些变更记录依然保存在上下文的变更追踪器中。当外部调用_context.SaveChanges()时,上下文会把所有追踪到的变更(包括之前事务里的Add/Update/Remove操作)一并提交到数据库,导致已回滚的变更被意外保存。
解决办法
方案1:使用短生命周期DbContext
在AddUser方法内部创建独立的DbContext实例,避免复用外部上下文。事务回滚后,新上下文会被销毁,外部上下文不会追踪到这些变更:
public void AddUser() { using (var newContext = new YourDbContext()) using (var transaction = newContext.Database.BeginTransaction()) { try { // 业务逻辑,使用newContext操作实体 newContext.SaveChanges(); transaction.Commit(); } catch { transaction.Rollback(); throw; } } }
方案2:手动清除变更追踪
在事务回滚后,清空上下文变更追踪器里的所有记录:
public void AddUser() { using (var transaction = _context.Database.BeginTransaction()) { try { // 业务逻辑 _context.SaveChanges(); transaction.Commit(); } catch { transaction.Rollback(); // 清除所有被追踪的实体变更 foreach (var entry in _context.ChangeTracker.Entries()) { switch (entry.State) { case EntityState.Added: entry.State = EntityState.Detached; break; case EntityState.Modified: case EntityState.Deleted: entry.Reload(); break; } } throw; } } }
这样外部调用_context.SaveChanges()时,只会提交日志的新增操作,不会包含之前回滚的变更。
内容的提问来源于stack exchange,提问作者Emilly
相关产品推荐
相关产品推荐

