EF Core 处理断开连接实体需显式设置 EntityState:新增用 Add(),更新推荐先查再改并还原并发戳,删除用 Remove();Attach() 默认 Unchanged,不适用于写操作;处理实体图应分步设子实体状态;AsNoTracking() 可提升只写性能。
EF Core 处理断开连接的实体,核心在于让 DbContext “知道”这个实体的状态(新增、修改、删除),而不是靠自动跟踪——因为断开连接的实体从未被当前上下文跟踪过。
断开连接的实体不会被自动追踪,必须手动告知 EF Core 它的意图:
context.Add(entity) 或 context.Entry(entity).State = EntityState.Added
context.Update(entity)(全部字段标记为已修改)或先查再改 + 手动设原始值context.Remove(entity) 或 context.Entry(entity).State = EntityState.Deleted
注意:Attach() 默认设为 Unchanged,仅适合只读场景;若要更新或删除,需额外设置状态。
直接 Update() 简单但会把所有属性标为“已修改”,哪怕没变;而先查再改虽精准,却可能覆盖原始并发戳导致并发检查失效。
推荐做法是:先查询实体,再赋值业务字段,最后手动恢复并发令牌的原始值:
var entity = await context.Products.FindAsync(dto.Id); entity.Name = dto.Name; entity.Price = dto.Price; // 关键:还原并发戳原始值,否则 SaveChanges 不校验 context.Entry(entity).OriginalValues["ConcurrencyStamp"] = dto.ConcurrencyStamp; await context.SaveChangesAsync();
一个根实体带多个导航属性(如 Order → OrderI
tems → Product)时,Attach() 或 Update() 的行为不一致:
Attach(root) 默认将子实体设为 Unchanged,即使你改了它们也不会保存Update(root) 会把整个图设为 Modified,但子实体主键为空时可能被误判为新增稳妥方式是分步处理:对每个子实体单独调用 Entry(x).State 显式指定状态,尤其注意已有 ID 的子项用 Modified,新子项用 Added。
如果只是批量导入或只写不读,不需要变更追踪,可在查询时用 AsNoTracking();对插入操作本身,Add() 不依赖跟踪,无需额外处理。但要注意:禁用跟踪后无法复用同一实例做后续更新,需重新查询。
基本上就这些。断开连接操作不复杂但容易忽略状态设定和并发戳还原,踩坑多在“以为 EF 会猜,其实它只认你写的 State”。