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);
相关推荐
一条大祥脚2 小时前
26.1.21 根号分治 相向双指针
java·开发语言·redis
涅小槃2 小时前
Carla仿真学习笔记(版本0.9.16)
开发语言·python·ros·carla
wujialaoer2 小时前
常用软件阿里源地址
开发语言·python
沐知全栈开发2 小时前
SVG 文本:深入解析与高效应用
开发语言
张丶大帅2 小时前
【走进Golang】
开发语言·后端·golang
明月看潮生2 小时前
编程与数学 03-008 《看潮企业管理软件》项目开发 01 需求分析 3-1
c#·.net·需求分析·erp·企业开发·项目实践·编程与数学
Sheep Shaun2 小时前
深入理解红黑树:从概念到完整C++实现详解
java·开发语言·数据结构·c++·b树·算法
楼田莉子2 小时前
CMake学习:入门及其下载配置
开发语言·c++·vscode·后端·学习
2501_944521592 小时前
Flutter for OpenHarmony 微动漫App实战:列表项组件实现
android·开发语言·javascript·flutter·ecmascript