Entity Framework SaveChanges()回滚:批量插入异常时回滚状态与人员数据
问题解答
当前代码无法回滚已保存至State表和Peoples表的数据
原因分析
- State数据的保存操作
context.SaveChanges();在插入人员数据之前就已执行,且该操作未被任何事务包裹,执行后数据直接持久化到数据库,无法回滚。 - 每个州的处理逻辑都在独立的
StateEntities上下文实例中,EF默认事务仅覆盖单次SaveChanges操作,State保存完成后就脱离了事务控制。 InsertPeople方法中的TransactionScope仅对当前批次的人员插入生效,每次批次完成后调用transactionScope.Complete()提交事务,之前批次的人员数据已经持久化;若后续批次抛出异常,只能回滚当前正在处理的批次,无法影响已提交的批次和State数据。
修改方案
要实现State数据和对应所有人员数据的原子性操作(要么全部成功,要么全部回滚),需要将State的保存和该州所有人员的插入操作纳入同一个事务范围内。以下是调整后的代码示例:
List<Peoples> PeopleList = new List<Peoples>(); int peopleCounter = 0; foreach (var stateVal in States) { // 为每个州创建全局事务范围 using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, new System.TimeSpan(0, 60, 0))) { using (var context = new StateEntities()) { State st = new State(); st.ID = stateVal.ID; st.Name = stateVal.Name; context.State.Add(st); context.SaveChanges(); // 此时操作处于事务范围内,未真正提交 if (stateVal.Peoples != null) { foreach (var _p in stateVal.Peoples) { Peoples _people = new Peoples(); _people.Name = _p.Name; _people.Age = _p.Age; _people.State_ID = stateVal.ID; PeopleList.Add(_people); peopleCounter++; if (peopleCounter == 100000) { InsertPeople(PeopleList, context); PeopleList.Clear(); peopleCounter = 0; } } // 处理剩余不足100k的人员数据 if (PeopleList.Count > 0) { InsertPeople(PeopleList, context); PeopleList.Clear(); peopleCounter = 0; } } } // 所有操作完成后提交事务 transactionScope.Complete(); } } private static void InsertPeople(List<Peoples> PeopleList, StateEntities context) { context.Configuration.AutoDetectChangesEnabled = false; context.Configuration.ValidateOnSaveEnabled = false; // 不再需要单独的TransactionScope,因为上下文已处于外层事务范围内 context.BulkInsert(PeopleList, options => options.BatchTimeout = 0); context.SaveChanges(); }
关键调整说明
- 将每个州的完整操作(State保存+所有人员插入)包裹在同一个
TransactionScope中,确保所有操作要么全部提交,要么全部回滚。 - 移除
InsertPeople方法内的独立事务,让批量插入操作继承外层的事务上下文。 - 补充处理循环结束后剩余的不足100k的人员数据,避免遗漏。
内容的提问来源于stack exchange,提问作者user3296355
相关产品推荐
相关产品推荐

