ef core 默认启用变更跟踪,影响性能;应使用 asnotracking()(须置于查询末尾)、全局配置 querytrackingbehavior.notracking 或禁用 autodetectchangesenabled 优化只读场景。

默认情况下,EF Core 会对所有查询结果做变更跟踪,哪怕你只读不改——这会吃内存、拖慢速度。只要确认后续不调用 SaveChanges(),就该关掉它。
AsNoTracking() 必须链在查询末尾才生效
很多人把 AsNoTracking() 放错位置,比如写在 Include() 前面或中间,结果无效。它只对紧邻的 IQueryable 生效,且必须在执行前(即 ToList()、FirstOrDefault() 等之前)调用。
- ✅ 正确:
context.Orders.AsNoTracking().Include(o => o.Customer).Where(...).ToList() - ❌ 错误:
context.Orders.Include(o => o.Customer).AsNoTracking().Where(...)(Include已触发内部跟踪逻辑) - ⚠️ 注意:如果用了
Select投影到匿名类型或 DTO,AsNoTracking()仍有效,但此时本就不参与跟踪——重点是避免实体类被加载后还被上下文管理
全局关闭 QueryTrackingBehavior 的两种注册方式
如果你的项目以只读为主(比如报表服务、API 查询层),硬编码每个查询加 AsNoTracking() 容易漏,直接设默认行为更可靠。
- 推荐方式(DI 注册时):
services.AddDbContextPool<yourdbcontext>(options => options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking))</yourdbcontext> - 备选方式(构造函数内):
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking,但要注意这会在每次实例化时重置,不如 DI 层统一 - ⚠️ 全局禁用后,个别需要更新的查询得手动补
AsTracking(),否则SaveChanges()不会识别改动
AsNoTracking() 后不能直接 SaveChanges,但能绕过它更新
关闭跟踪不等于不能改数据——只是 EF Core 不再自动感知变化。你仍可手动 Attach + 修改状态,或用原生 SQL / ExecuteUpdateAsync()。
- ❌ 以下无效:
var user = context.Users.AsNoTracking().First(); user.Name = "new"; context.SaveChanges();(无变更记录,不会生成 UPDATE) - ✅ 替代方案一(Attach 后设状态):
context.Users.Attach(user); context.Entry(user).State = EntityState.Modified; context.SaveChanges(); - ✅ 替代方案二(EF Core 7+):
context.Users.Where(u => u.Id == id).ExecuteUpdateAsync(u => u.SetProperty(x => x.Name, "new")) - ⚠️ 关键点:非跟踪查询返回的实体对象,和上下文之间没有“绑定关系”,Attach 是重建这个关系的显式操作
别忘了 AutoDetectChangesEnabled 这个隐藏开销
即使关了跟踪,EF Core 在每次 SaveChanges() 前仍可能自动调用 DetectChanges() 扫描所有已跟踪实体——而你在只读场景根本没加载任何要跟踪的实体。这时候关掉它能省下毫秒级 CPU 时间。
- 适用场景:纯写入服务(如日志入库、消息同步),完全不查数据只 Insert
- 做法:
context.ChangeTracker.AutoDetectChangesEnabled = false;,插入完成后如需其他操作再手动DetectChanges() - ⚠️ 和
AsNoTracking()不同,这个设置影响的是变更检测阶段,不是查询阶段;两者可以共存,但解决的问题层面不同











