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);
相关推荐
草履虫建模14 小时前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
naruto_lnq16 小时前
分布式系统安全通信
开发语言·c++·算法
.房东的猫17 小时前
ERP(金蝶云星空)开发【安装篇】
c#
学嵌入式的小杨同学17 小时前
【Linux 封神之路】信号编程全解析:从信号基础到 MP3 播放器实战(含核心 API 与避坑指南)
java·linux·c语言·开发语言·vscode·vim·ux
Re.不晚17 小时前
Java入门17——异常
java·开发语言
精彩极了吧18 小时前
C语言基本语法-自定义类型:结构体&联合体&枚举
c语言·开发语言·枚举·结构体·内存对齐·位段·联合
南极星100519 小时前
蓝桥杯JAVA--启蒙之路(十)class版本 模块
java·开发语言
baidu_2474386119 小时前
Android ViewModel定时任务
android·开发语言·javascript
Dev7z19 小时前
基于 MATLAB 的铣削切削力建模与仿真
开发语言·matlab
不能隔夜的咖喱19 小时前
牛客网刷题(2)
java·开发语言·算法