C#AsNoTracking()详解

一、基本含义

AsNoTracking() 是 Entity Framework (EF) 和 Entity Framework Core (EF Core) 中的一个 LINQ 查询扩展方法,用于指示 EF 不跟踪查询返回的实体对象。

二、主要作用

1. 性能优化

cs 复制代码
// 使用跟踪(默认)
var trackedUsers = context.Users.Where(u => u.IsActive).ToList();
// EF 会创建这些对象的快照并跟踪变化

// 使用非跟踪
var noTrackedUsers = context.Users.AsNoTracking()
                                  .Where(u => u.IsActive)
                                  .ToList();
// EF 不跟踪这些对象,减少内存占用和性能开销

2. 避免状态管理

  • 跟踪实体:EF 会监视实体属性的变化

  • 非跟踪实体:EF 将实体视为"只读",不监视变化

三、工作原理对比

EF、EFCore中的查询默认是跟踪的,使用AsNoTracking()禁用跟踪查询时,对实体类的更新不会起作用,但是对于大数据量的查询会显著提升查询效率。

特性 跟踪查询 (默认) 非跟踪查询 (AsNoTracking)
变更跟踪 ✅ 启用 ❌ 禁用
内存使用 较高(维护状态) 较低
查询速度 稍慢 更快
标识解析 ✅ 有 ❌ 无
自动保存 ✅ 支持 ❌ 不支持

四、主要使用场景

1. 只读数据展示

cs 复制代码
// 报表、统计、数据展示等只读场景
public List<OrderDto> GetRecentOrders(int days)
{
    return context.Orders
        .AsNoTracking()
        .Where(o => o.OrderDate >= DateTime.Now.AddDays(-days))
        .Select(o => new OrderDto
        {
            Id = o.Id,
            CustomerName = o.Customer.Name,
            TotalAmount = o.TotalAmount
        })
        .ToList();
}

2. 大数据量查询

cs 复制代码
// 导出大量数据时使用
public async Task ExportUsersToCsv(string filePath)
{
    var users = await context.Users
        .AsNoTracking()
        .Where(u => u.IsActive)
        .ToListAsync();
    
    // 处理大量数据,避免内存压力
    await WriteToCsv(users, filePath);
}

3. DTO/ViewModel 映射

cs 复制代码
public UserProfileDto GetUserProfile(int userId)
{
    var user = context.Users
        .AsNoTracking()
        .Include(u => u.Addresses)
        .Include(u => u.Orders)
        .FirstOrDefault(u => u.Id == userId);
    
    // 转换为 DTO,不需要跟踪
    return _mapper.Map<UserProfileDto>(user);
}

五、注意事项

1. 更新非跟踪实体

cs 复制代码
// 错误:直接修改不会保存
var user = context.Users.AsNoTracking().First(u => u.Id == 1);
user.Name = "New Name";
context.SaveChanges(); // 不会更新!

// 正确:需要先附加到上下文
var user = context.Users.AsNoTracking().First(u => u.Id == 1);
user.Name = "New Name";
context.Users.Update(user); // 或者 Attach + 设置状态
context.SaveChanges();

2. 导航属性延迟加载

cs 复制代码
// 非跟踪实体不支持延迟加载
var order = context.Orders.AsNoTracking().First();
// var items = order.OrderItems; // 这会抛出异常!

// 解决方案:使用 Include 或显式加载
var order = context.Orders
    .AsNoTracking()
    .Include(o => o.OrderItems)
    .First();

3. 性能权衡

**// 场景决定是否使用

  • 需要多次更新同一实体 ❌ 不要使用 AsNoTracking
  • 只读操作、数据展示 ✅ 推荐使用 AsNoTracking
  • 复杂查询链 ✅ 考虑使用 AsNoTracking**

六、最佳实践建议

  1. 默认使用非跟踪 :除非明确需要跟踪,否则优先使用 AsNoTracking()

  2. 明确查询意图:让代码清晰表达这是只读操作

  3. 结合性能监控:在大数据量场景下测试性能差异

  4. 注意生命周期:避免将非跟踪实体传递到需要跟踪的代码中

  5. 文档说明:在团队项目中建立统一的使用规范

核心原则 :如果知道实体需要被修改,就不要使用 AsNoTracking()。如果因为性能原因使用了非跟踪查询,更新时需要显式地将实体附加到上下文并标记为已修改状态。

七、使用

1、当然 如果代码中对查询和更新使用了两个独立的ORM框架 那么更新就不会受不跟踪的影响,

如下

cs 复制代码
// EF Core 查询(使用 AsNoTracking)
var stkTrayDtlLists = await (await _stkTrayDtlRepository.GetQueryableAsync())
    .AsNoTracking()  // ← EF Core 的变更跟踪设置
    .Where(...)
    .ToListAsync();

// SqlSugar 批量更新
await sqlSugarClient.Fastest<WmsStkTrayDtl>()
    .BulkUpdateAsync(prepareUpdateStkTrayDtl);  // ← SqlSugar 的批量更新
  • EF Core 的变更跟踪:内置的上下文跟踪机制

  • SqlSugar 的 BulkUpdate :直接根据实体属性值生成 SQL 语句并执行、SqlSugar 不依赖变更跟踪

2、显式更新(更新成功):

UpdateManyAsync会将它实体类附加到DbContext并标记为已修改。

cs 复制代码
// EF Core 查询(使用 AsNoTracking)
var stkTrayDtlLists = await (await _stkTrayDtlRepository.GetQueryableAsync())
    .AsNoTracking()  // ← EF Core 的变更跟踪设置
    .Where(...)
    .ToListAsync();
await _stkTrayDtlRepository.UpdateManyAsync(prepareUpdateStkTrayDtl);
相关推荐
橙露3 分钟前
Java并发编程进阶:线程池原理、参数配置与死锁避免实战
java·开发语言
froginwe113 分钟前
C 标准库 - `<float.h>`
开发语言
2501_9160088914 分钟前
深入解析iOS机审4.3原理与混淆实战方法
android·java·开发语言·ios·小程序·uni-app·iphone
wxin_VXbishe20 分钟前
C#(asp.net)学员竞赛信息管理系统-计算机毕业设计源码28790
java·vue.js·spring boot·spring·django·c#·php
Dimpels34 分钟前
CANN ops-nn 算子解读:AIGC 批量生成中的 Batch 处理与并行算子
开发语言·aigc·batch
blueSatchel43 分钟前
U-Boot载入到DDR过程的代码分析
linux·开发语言·u-boot
无小道1 小时前
QT——QFIie和QFileInfo文件类
开发语言·qt·命令模式
踢足球09291 小时前
寒假打卡:2026-2-7
java·开发语言·javascript
薛定谔的猫喵喵2 小时前
基于PyQt5的视频答题竞赛系统设计与实现
开发语言·qt·音视频
岱宗夫up2 小时前
Python 数据分析入门
开发语言·python·数据分析