博客 (2)

  • 在代码中添加“不跟踪”(No-Tracking)功能,以提高查询性能(避免实体状态跟踪)。

  • 确保后续操作无需更新返回的实体(如没有 SaveChanges 操作)。

  • 在条件(Where)、排序(OrderBy、OrderByDescending)、分页(Skip、Take)等之前添加 .AsNoTracking(),如:db.Table.AsNoTracking().Where(...).ToList()。

  • 如果查询中包含导航属性,它们也会因主查询的不跟踪而保持不跟踪状态。

  • AsNoTracking() 适用于查询简单无嵌套关系,若查询包含 Include/ThenInclude,建议用 AsNoTrackingWithIdentityResolution() 代替,后者更适合处理树形结构或循环引用数据。

  • 添加(Add/AddRange)、修改、删除(Remove/RemoveRange)等有 SaveChanges 操作的场景不能加 .AsNoTracking()。

  • 查询时是否需要加 .AsNoTracking() 参考下表:

数据查询操作体系

├── 限定符 (Any, All)    ✔️ 推荐

├── 聚合函数 (Count, Sum, Min, Max, Average)    ❌ 不需要(但在分页逻辑中 Count 与 ToList 使用共同条件筛选时建议加)

├── 集合操作 (Distinct, Union)    ⚠️ 推荐用于只读

├── 元素操作 (First, Single)    ⚠️ 推荐用于只读

└── 即时执行操作 (ToList, ToArrayToDictionary)    ⚠️ 推荐用于只读



xoyozo 4 个月前
558

AsNoTracking 设置未追踪查询

var customers = dbContext.Customers.AsNoTracking().ToList();

这对于只读查询非常有用,因为它可以减少内存使用并提高性能,因为它不需要维护实体的更改跟踪信息。

何时需要 AsNoTracking()?


ExecuteDelete 和 ExecuteUpdate 批量操作

context.Logs.Where(c => c.Time < new DateTime(2000, 1, 1)).ExecuteDelete();

从 EF Core 7 开始,ExecuteDelete 和 ExecuteUpdate 是官方原生支持的批量操作方法。直接操作数据库,不需要调用 SaveChanges():不加载实体到内存,减少内存消耗和网络往返。

若需要分页删除和大批量插入,或在高频、大规模场景,推荐使用 Zack.EFCore.Batch

context.Logs.Where(c => c.Time < new DateTime(2000, 1, 1)).DeleteRangeAsync(batchSize: 1000);


考虑用 Union 代替 OR

// Where 后行数多时(如分页前)用 OR
var q = db.dt_crm__contract.AsNoTracking();
q = q.Where(c => c.dt_crm__customer.SalesmanId == uid || myIns.Contains(c.IndustryId));

// 用于合并的 q1、q2 的行数少时用 Union
var q1 = db.dt_crm__contract.AsNoTracking().Where(c => c.dt_crm__customer.SalesmanId == uid);
var q2 = db.dt_crm__contract.AsNoTracking().Where(c => myIns.Contains(c.IndustryId));
var q = q1.Union(q2);


“ToDictionary + Count”之前先 Select

// 不推荐
var dic = q.GroupBy(c => c.Date)
    .ToDictionary(k => k.Key, v => v.Count());
// 推荐
var dic = q.GroupBy(c => c.Date)
    .Select(g => new { g.Key, Count = g.Count() })
    .ToDictionary(k => k.Key, v => v.Count);



xoyozo 4 个月前
810