小弟是第一次接触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的查询
- 控制什么时间调用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
- 获取不带变动跟踪的实体查询
为上文AsNoTracking所述,不再重复。 - 单个实体的变动跟踪信息及操作
使用状态属性
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实体对象变动跟踪