EF Core 处理断开连接实体需显式设置 EntityState:新增用 Add(),更新推荐先查再改并还原并发戳,删除用 Remove();Attach() 默认 Unchanged,不适用于写操作;处理实体图应分步设子实体状态;AsNoTracking() 可提升只写性能。

EF Core 处理断开连接的实体,核心在于让 DbContext “知道”这个实体的状态(新增、修改、删除),而不是靠自动跟踪——因为断开连接的实体从未被当前上下文跟踪过。
明确设置 EntityState 是关键步骤
断开连接的实体不会被自动追踪,必须手动告知 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 → OrderItems → Product)时,Attach() 或 Update() 的行为不一致:
-
Attach(root)默认将子实体设为Unchanged,即使你改了它们也不会保存 -
Update(root)会把整个图设为Modified,但子实体主键为空时可能被误判为新增
稳妥方式是分步处理:对每个子实体单独调用 Entry(x).State 显式指定状态,尤其注意已有 ID 的子项用 Modified,新子项用 Added。
插入或更新前可禁用跟踪提升性能
如果只是批量导入或只写不读,不需要变更追踪,可在查询时用 AsNoTracking();对插入操作本身,Add() 不依赖跟踪,无需额外处理。但要注意:禁用跟踪后无法复用同一实例做后续更新,需重新查询。
基本上就这些。断开连接操作不复杂但容易忽略状态设定和并发戳还原,踩坑多在“以为 EF 会猜,其实它只认你写的 State”。










