博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Entity Framework 小技巧四 —— 如何使用NoTracking查询得到Detached状态的实体?

有时我们的实体只需要显示,无需更新,所以为了提高性能,我们不需要实体被EF context追踪。此时可以使用NoTracking的查询来得到实体,这样实体的状态会是Detached状态。

 

在EF3.5 SP1和EF 4中,我们可以这样来进行NoTracking查询:

using (var context = new MyObjectContext())
{
    context.People.MergeOption 
= System.Data.Objects.MergeOption.NoTracking;
    var list 
= context.People.Where(p => PersonID > 100).ToList();
}

或使用ExecuteStoreQuery API来直接调用SQL命令来得到实体:

using (var context = new MyObjectContext())
{
    var query 
= context.ExecuteStoreQuery<Parent>("SELECT * FROM Parent WHERE Parent.ParentID > @ID""TestDBEntities.Parents"System.Data.Objects.MergeOption.NoTrackingnew SqlParameter("@ID"100));
}

或使用Load方法得到相关实体:

using (var context = new MyObjectContext())
{
    context.ContextOptions.LazyLoadingEnabled 
= false;
    var person 
= context.People.First();
    person.Courses.Load(System.Data.Objects.MergeOption.NoTracking);
}

在EF 4.1中,我们可以使用新增的AsNoTracking方法:

using (var context = new MyDbContext())
{
     var people 
= context.People.Where(p => p.PersonID > 100).AsNoTracking().ToList();
}

 

这里的AsNoTracking方法内部调用了过去的ObjectQuery.MergeOption来实现NoTracking查询。此方法(DbHelpers.CreateNoTrackingQuery)是AsNoTracking方法的核心:

public static IQueryable CreateNoTrackingQuery(ObjectQuery query)
{
    IQueryable queryable 
= query;
    ObjectQuery query2 
= (ObjectQuery) queryable.Provider.CreateQuery(queryable.Expression);
    query2.MergeOption 
= MergeOption.NoTracking;
    
return query2;
}

 

再为大家介绍NoTracking得到的Detached实体的一个小问题。由NoTracking查询得到的实体和我们直接调用Detach方法得到的实体不同。前者内部会仍然保留一个对当前context的引用,以便在调用Load方法可以explicitly load相关的实体。而后者则完全脱离了相应的context,所以属于真正的Detached状态。细心的读者可能会觉得NoTracking得到的 Detached实体会导致context一直被引用,从而不能及时被垃圾处理(GC)。确实,这个被产品组证实是by design的,如果context不被引用,则explicit load则无法被支持。