EF-AsNoTracking()

本文深入探讨了EF中AsNoTracking方法的使用,解释了如何利用该方法优化查询性能,避免不必要的变动跟踪。同时,文章详细讲解了EF实体对象变动跟踪的机制,包括变动跟踪快照和变动跟踪代理,以及如何手动控制变动跟踪。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

小弟是第一次接触EF这个ORM,在使用时发现甚是方便尽管有性能上的担忧。但是我现在接触到的都是简单使用,简单到我甚至不需要动脑子,所以一直在找深入使用的东西。偶然发现了一种AsNoTracking()的用法,尽管能够见名知意但我觉得还是有必要研究一下其具体意义。果不其然,它带我打开了深入理解EF的一扇门。
跟踪数据与非跟踪数据的内容,如果我能了解一些并合理使用的话,对我来说就算是EF学习的进步之一了,更何况还能优化EF使用性能。
下面大部分信息都来自于对他人文章的参考,本人仅作学习。


先说明白引诱我入坑的AsNoTracking()。

DBSet.AsNoTracking() 获取非跟踪数据

AsNoTracking 称之为获取不带变动跟踪的实体查询

在一些情况下,我们只需要查询返回一个只读的数据记录,而不会对数据记录进行任何的修改。这种时候不希望Entity Framework进行不必要的状态变动跟踪,可以使用Entity Framework的AsNoTracking方法来查询返回不带变动跟踪的查询结果。

using (var ctx = new PortalContext())
{
    foreach (var province in ctx.Provinces.AsNoTracking())
    {
        Console.WriteLine(province.ProvinceName);
    }
}

以上代码中使用AsNoTracking方法查询返回无变动跟踪的Province的DbSet,由于是无变动跟踪,所以对返回的Province集中数据的任何修改,在SaveChanges()时,都不会提交到数据库中。

AsNoTracking是定义在IQueryable中的扩展方法,所以也可以用于LINQ表达式查询。

using (var ctx = new PortalContext())
{
    var query = from p in ctx.Provinces.AsNoTracking()
                where p.ProvinceID > 10
                select p;
    foreach (var province in query)
    {
        Console.WriteLine(province.ProvinceName);
    }
}

注:使用AsNoTracking需要添加引用命名空间using System.Data.Entity。


基于此进行拓展学习:

EF实体对象变动跟踪

Entity Framework 通过DbContext.ChangeTracker对实体对象的变动进行跟踪,实现跟踪的方式有两种:变动跟踪快照和变动跟踪代理。

  • 变动跟踪快照:前面几篇随笔的示例都是通过实体对象变动快照跟踪来实现数据操作的,POCO模型不包含任何逻辑去通知Entity Framework实体类属性的变动。Entity Framework在第一次对象加载到内存中时进行一次快照,添加快照发生在返回一次查询或添加一个对象到DbSet中时。当Entity Framework需要知道对象的变动时,将先把当前实体与快照中的对象进行扫描对比。实现扫描对比的方法是调用DbContext.ChangeTracker的DetectChanges方法。
  • 变动跟踪快照:前面几篇随笔的示例都是通过实体对象变动快照跟踪来实现数据操作的,POCO模型不包含任何逻辑去通知Entity Framework实体类属性的变动。Entity Framework在第一次对象加载到内存中时进行一次快照,添加快照发生在返回一次查询或添加一个对象到DbSet中时。当Entity Framework需要知道对象的变动时,将先把当前实体与快照中的对象进行扫描对比。实现扫描对比的方法是调用DbContext.ChangeTracker的DetectChanges方法。

Entity Framework Code First中能够自动调用DbContext.ChangeTracker.DetectChanges的方法:

  • DbSet.Add
  • DbSet.Find
  • DbSet.Remove
  • DbSet.Attach
  • DbSet.Local
  • DbContext.SaveChanges
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries
  • 任何在DbSet上进行LINQ的查询
  1. 控制什么时间调用DetectChanges

大部分的实例对象的变动调整需要在Entity Framework进行SaveChanges时才会知道,但也可以根据需要调用变动跟踪获取当前对象的状态。

Entity Framework Code First的DbContext.DetectChanges在检测实例对象的变动时,大部分情况不会有性能的问题。但当有大量的实例对象在内存中,或DbContext有大量的操作时,自动的DetectChanges行为可能会一定程度的影响性能。

Entity Framework提供关闭自动的DetectChanges的功能,在需要的时候进行手动调用。如果考虑内存中的跟踪数量太大问题,就可以手动跟踪。

using (var ctx = new PortalContext())
{
    ctx.Configuration.AutoDetectChangesEnabled = false;

    var province = ctx.Provinces.Find(1);
    province.ProvinceName = "测试";

    Console.WriteLine("Before DetectChanges:{0}", ctx.Entry(province).State);

    ctx.ChangeTracker.DetectChanges();

    Console.WriteLine("After DetectChanges:{0}", ctx.Entry(province).State);
}
// 运行结果
Before DetectChanges:Unchanged
After DetectChanges:Modified
  1. 获取不带变动跟踪的实体查询
    为上文AsNoTracking所述,不再重复。
  2. 单个实体的变动跟踪信息及操作
    使用状态属性
using (var ctx = new PortalContext())
{
    var province = ctx.Provinces.Find(10);
    DbEntityEntry<Province> entry = ctx.Entry(province);
    Console.WriteLine("Before Edit: {0}", entry.State);
    province.ProvinceName = "Test";
    ctx.ChangeTracker.DetectChanges();
    Console.WriteLine("After Edit: {0}", entry.State);
}
// 运行结果
Before Edit: Unchanged
After Edit: Modified

注:DbEntityEntry需要引用命名空间using System.Data.Entity.Infrastructure;


PS:以上内容为我觉得常用的功能,想要看更多可以去看下面我的转载来源连接。
转载自:EF实体对象变动跟踪

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值