You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core中.Include与.AsNoTracking的正确用法及相关疑问

AsNoTracking() 使用位置与性能问题解答

背景

早年不少技术帖子对AsNoTracking()的使用位置存在争议:部分观点认为需在查询物化前多次指定,部分则认为仅在查询顶部设置即可作用于全局,但这类旧帖大多不适用于EF6及以上版本,尤其是EF Core场景。以下是包含多实体关联、Include与Union的复杂查询示例:

var dataSet1 = _dbContext.MarketTransactions.AsNoTracking()
    .AsSplitQuery()
    .Include(a => a.Commodity).ThenInclude(a => a.Product).AsNoTracking()
    .Include(a => a.Contract).ThenInclude(a => a.Customer).AsNoTracking()
    .Include(a => a.Offer).ThenInclude(a => a.Customer).AsNoTracking()
    .Where(f => f.IsActive && (f.Source == EMarketTransactionSource...))
    .GroupJoin(
        _dbContext.HedgeAccounts.AsNoTracking(), 
        transaction => transaction.MarketAccount,
        hedgeAcct => hedgeAcct.Account, 
        (transaction, hedgeAcct) => new { Transaction = transaction, HedgeAccounts = hedgeAcct }) 
    .SelectMany(
        x => x.HedgeAccounts.DefaultIfEmpty(), 
        (x, hedgeAcct) => new { x.Transaction, HedgeAccount = hedgeAcct })
    .Select(f => new ...);

var dataSet2 = _dbContext.Set<OfferMonitoring>().AsNoTracking()
    .AsSplitQuery()
    .Include(a => a.Offer).ThenInclude(a => a.Commodity).ThenInclude(a => a.Product).AsNoTracking()
    .Include(a => a.Offer).ThenInclude(a => a.Customer).AsNoTracking()
    .Where(f => f.Action.Value == EOfferMonitorAction....)
    .Select(f => new ...);

var query = dataSet1.Union(dataSet2);

var total = await query.CountAsync(cancellationToken);

query = query.OrderByDescending(x => x.UpdatedOn ?? x.CreationDate);
query = query.GetPagedQuery(request.Filter.Start, request.Filter.Limit);
var items = await query.ToListAsync(cancellationToken);

问题解答

1. AsNoTracking() 应在查询开头全局设置一次,还是在每个Include或Join子句前设置?

在EF Core 3.x及以上版本中:

  • 根查询起始位置调用一次AsNoTracking(),会自动作用于所有后续Include/ThenInclude加载的关联实体,无需在每个Include分支重复调用。
  • 对于GroupJoin/Join中单独从DbContext获取的独立查询源(比如示例中的_dbContext.HedgeAccounts),需要单独添加AsNoTracking(),因为这类子查询是独立的查询根,不会继承主查询的跟踪设置。
  • 对于Union这类组合查询,每个分支查询(dataSet1、dataSet2)需要各自设置AsNoTracking(),因为它们是独立构建的查询链。

简言之:根查询+独立子查询源各设置一次即可,无需在Include后重复调用。

2. 两种用法的性能影响如何,尤其是结果集较大的情况下?

  • 重复调用AsNoTracking()不会带来性能提升:EF Core内部会忽略重复的无跟踪设置(只要已启用无跟踪,后续调用不会改变状态),只会增加代码冗余。
  • 漏加独立查询源的AsNoTracking()会显著增加开销:如果关联的子查询实体被跟踪,EF需要维护每个实体的状态快照,结果集越大,内存占用越高,后续GC压力也会越大,查询整体性能会下降。
  • 正确的用法(根查询+独立子查询源各一次)是性能最优的:既避免了状态跟踪的额外内存与CPU开销,又保持了代码简洁。

3. Entity Framework Core不同版本间在行为或性能上是否存在差异?

  • EF Core 1.x-2.x:早期版本中AsNoTracking()的作用范围有限,Include加载的实体可能需要单独设置,子查询也不会继承主查询的跟踪设置,这也是早年帖子争议的核心原因。
  • EF Core 3.x及以上:官方明确了AsNoTracking()的作用规则,根查询的设置会覆盖所有Include/ThenInclude的关联实体;同时对无跟踪查询做了底层优化,减少了不必要的状态跟踪逻辑,性能比早期版本提升明显,尤其是大结果集场景下内存占用差异更显著。
  • 注意:即使在EF Core 3.x+中,独立子查询源仍需单独设置AsNoTracking(),不会自动继承主查询的设置。

内容的提问来源于stack exchange,提问作者nop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 18:35:31