DbContext SaveChanges遇验证错误,修改数据后仍重复报错
问题描述
通过继承自DbContext的eBusinessEntities实现了CSV数据更新方法,测试错误场景时上传了包含无效State字段的Test.Entry@aol.com数据,收到验证错误:
Test.Entry@aol.com has the following validation errors:-"Field: "State", Error: "The field State must be a string or array type with a maximum length of '2'."
但修复数据后,甚至上传完全不同的新数据(如DifferentEntry@gmail.com)时,仍抛出针对Test.Entry@aol.com的相同验证错误。调试确认当前创建的Entry数据完全匹配CSV内容,但调用SaveChanges()时仍触发旧错误。
相关代码
异常捕获代码
try { var success = _dbContext.SaveChanges() > 0; return success ? "Done! Please reload the page" : "Failed to update entries"; } catch (DbEntityValidationException e) { var output = "FAILED TO SAVE CHANGES TO DATABASE. Please see the following validation errors:<br/>"; foreach (var eve in e.EntityValidationErrors) { output += String.Format("{0} has the following validation errors:", eve.Entry.Property("Email").CurrentValue); foreach (var ve in eve.ValidationErrors) { output += String.Format("- Field: \"{0}\", Error: \"{1}\"<br/>", ve.PropertyName, ve.ErrorMessage); } } return output; }
仓储层代码
public class EntriesRepository : CachableBase, IEntriesRepository { private readonly IeBusinessEntities _ieBusinessEntities; public EntriesRepository(IeBusinessEntities ieBusinessEntities) { _ieBusinessEntities = ieBusinessEntities; } public string UpdateEntries(List<Entry> entries) { foreach (var entry in entries) { _ieBusinessEntities.Entries.Add(entry); } // 上述异常捕获代码,此处替换为_dbContext为_ieBusinessEntities try { var success = _ieBusinessEntities.SaveChanges() > 0; return success ? "Done! Please reload the page" : "Failed to update entries"; } catch (DbEntityValidationException e) { var output = "FAILED TO SAVE CHANGES TO DATABASE. Please see the following validation errors:<br/>"; foreach (var eve in e.EntityValidationErrors) { output += String.Format("{0} has the following validation errors:", eve.Entry.Property("Email").CurrentValue); foreach (var ve in eve.ValidationErrors) { output += String.Format("- Field: \"{0}\", Error: \"{1}\"<br/>", ve.PropertyName, ve.ErrorMessage); } } return output; } } // 其他方法... }
原因分析
DbContext是上下文实例级别的状态跟踪容器:
- 第一次上传错误数据时,
Test.Entry@aol.com对应的Entry实体已被添加到当前DbContext实例的变更跟踪器中,处于Added状态。 - 即使后续上传新数据,只要使用的是同一个DbContext实例,之前添加的错误实体不会自动被清理。调用
SaveChanges()时,DbContext会尝试提交所有处于跟踪状态的实体变更,包括那个未被移除的错误实体,因此会重复触发相同的验证错误。
解决方案
1. 使用独立的DbContext实例(推荐)
将DbContext的生命周期配置为请求域(Scoped)(如果是依赖注入场景),确保每次CSV上传请求都使用全新的DbContext实例,从根源避免旧实体残留。
2. 手动清理上下文残留实体
在UpdateEntries方法处理新数据前,主动移除所有未提交的Entry实体:
public string UpdateEntries(List<Entry> entries) { // 清理所有处于Added状态的Entry实体 var addedEntries = _ieBusinessEntities.Entries.Local .Where(e => _ieBusinessEntities.Entry(e).State == EntityState.Added) .ToList(); foreach (var entry in addedEntries) { _ieBusinessEntities.Entry(entry).State = EntityState.Detached; } // 继续添加新数据 foreach (var entry in entries) { _ieBusinessEntities.Entries.Add(entry); } // 后续的SaveChanges和异常捕获逻辑... }
3. 捕获错误后立即清理错误实体
在DbEntityValidationException捕获块中,将验证失败的实体从上下文跟踪中移除,避免后续提交时再次触发错误:
catch (DbEntityValidationException e) { var output = "FAILED TO SAVE CHANGES TO DATABASE. Please see the following validation errors:<br/>"; foreach (var eve in e.EntityValidationErrors) { output += String.Format("{0} has the following validation errors:", eve.Entry.Property("Email").CurrentValue); foreach (var ve in eve.ValidationErrors) { output += String.Format("- Field: \"{0}\", Error: \"{1}\"<br/>", ve.PropertyName, ve.ErrorMessage); } // 将错误实体从上下文跟踪中分离 _ieBusinessEntities.Entry(eve.Entry.Entity).State = EntityState.Detached; } return output; }
内容的提问来源于stack exchange,提问作者Erica Stockwell-Alpert
相关产品推荐
相关产品推荐

