使用Entity Framework 6修改记录时EntityState.Modified未被设置的问题
这个问题我之前也碰到过!其实是EF6的快照式变更跟踪机制在起作用:
当你通过dbContext.MyUsers.Find(8)获取实体时,EF上下文已经开始跟踪这个实体了,它会悄悄保存一份实体所有属性的「原始快照」。当你修改UserName属性后,EF并不会立刻把实体的EntityState改成Modified——它只是在内部的变更跟踪器里标记这个具体属性被修改了,而实体的整体状态仍然是Unchanged。
直到你调用SaveChanges()时,EF才会对比原始快照和当前实体的属性值,生成只更新变化字段的SQL语句,所以你的记录能成功更新,但在你重写的SaveChanges方法里检查EntityState.Modified时,自然拿不到true。
而新增实体时,你是通过Add方法把实体加入上下文,此时实体的EntityState会直接被设为Added,所以你的判断逻辑能正常工作。
针对这个情况,有两种常用的处理方式,推荐第一种:
1. 检查变更跟踪器的属性修改状态(推荐)
在重写的SaveChanges方法里,不要只看实体的整体EntityState,而是遍历变更跟踪器里的所有条目,检查是否有属性被修改(即使实体状态是Unchanged)。
假设你有一个定义了审计字段的接口:
public interface IAuditableEntity { DateTime CreatedDate { get; set; } DateTime ModifiedDate { get; set; } }
重写SaveChanges的代码可以这样写:
public override int SaveChanges() { var currentUtcTime = DateTime.UtcNow; foreach (var entry in ChangeTracker.Entries<IAuditableEntity>()) { var auditableEntity = entry.Entity; switch (entry.State) { case EntityState.Added: // 新增实体时设置创建和修改日期 auditableEntity.CreatedDate = currentUtcTime; auditableEntity.ModifiedDate = currentUtcTime; break; case EntityState.Modified: // 实体被手动标记为Modified时,更新修改日期 auditableEntity.ModifiedDate = currentUtcTime; break; case EntityState.Unchanged: // 检查实体是否有属性被修改(即使状态是Unchanged) if (entry.HasChanges()) { auditableEntity.ModifiedDate = currentUtcTime; // 手动标记ModifiedDate为已修改,否则EF不会把它加入更新语句 entry.Property(e => e.ModifiedDate).IsModified = true; } break; } } return base.SaveChanges(); }
这里的关键是entry.HasChanges()方法——它能判断当前实体是否有任何属性和原始快照不一致,不管实体的EntityState是什么。
2. 手动标记实体为Modified(不推荐)
如果你不在乎生成全字段更新的SQL语句,可以在修改属性后手动把实体状态设为Modified:
var result = dbContext.MyUsers.Find(8); result.UserName = "Tony Baloney"; // 手动标记实体为Modified dbContext.Entry(result).State = EntityState.Modified; db.SaveChanges();
但这种方式会让EF生成更新所有字段的SQL,而不是只更新变化的UserName,可能会影响性能或者覆盖其他未预期的字段变更,所以一般不推荐。
内容的提问来源于stack exchange,提问作者John S

