Entity Framework 4.1 DbContext使用记之一——如何查找实体? DbSet.Find函数的使用与实现

随着EF4.1 RC上周的发布MSDN EF论坛的帖子也越来越多。相信大家会对EF4.1的一些新功能感兴趣。之后会为大家带来一系列的文章,与大家分享下我学习EF4.1的一些经验与感想。

顺便提一句,EF4.1的MSDN文档已经发布, http://msdn.microsoft.com/en-us/library/gg696172(v=VS.103).aspx。这一系列文章,可能需要您对EF有一些初级的认识。头一次写这样介绍性的文章,大家多提意见!

[4.13添加]EF4.1 RTW 版本也发布啦,http://www.microsoft.com/downloads/en/details.aspx?FamilyID=b41c728e-9b4f-4331-a1a8-537d16c6acdf&displaylang=en

 

第一篇为大家带来新的API,DbSet<>.Find()

 

过去我们常常用Where或First(FirstOrDefault)方法来查找对应的实体,比如:

var people = from p in context.People
             
where p.Name.StartsWith("M")
             select u;

var people = context.People.FirstOrDefault(p => p.Name == "Michael");  

这样查找的缺点是:即使我们相应的实体已经被ObjectContext缓存,EF还是会去执行数据库访问,而数据库访问是被普遍认为比较耗费性能的。

 

EF4.1为我们提供了一个新的API: DbSet<>.Find()。它可以帮助我们通过主键来查找对应的实体。并且如果相应的实体已经被ObjectContext缓存,EF会在缓存中直接返回对应的实体,而不会执行数据库访问。

 

比如我们查找主键PersonID为1的Person实体:

var person = context.People.Find(1);

 

也可用于联合主键(比如查找主键PersonID=1, PersonName="Michael"的实体):

 

var person = context.People.Find(1"Michael");

 

注意:此处输入联合主键的次序需要按照我们定义改实体类时声明主键的次序。

 

和之前直接用Where或First的调用不同,Find函数对于刚刚新增的实体也能找到:

 

using (var context = new MyContext())
{
    context.People.Add(
new People { PersonID = 100 });
    var newPerson 
= context.People.Find(100);
}

 

 

了解了Find函数的基本用法后,让我们来看看Find函数是如何实现的。这里我们借用了.NET Reflector来查看EF4.1的最重要的程序集EntityFramework.dll。

Find函数首先调用了EF的内部实现:

 

public TEntity Find(params object[] keyValues)
{
    
this.InternalContext.DetectChanges(false);
    WrappedEntityKey key 
= new WrappedEntityKey(this.EntitySet, this.EntitySetName, keyValues, "keyValues");
    
object obj1 = this.FindInStateManager(key);
    
if (obj1 != null)
    {
        
goto Label_003E;
    }
    
object obj2 = this.FindInStore(key, "keyValues");
    
if ((obj2 != null&& !(obj2 is TEntity))
    {
        
throw Error.DbSet_WrongEntityTypeFound(obj2.GetType().Name, typeof(TEntity).Name);
    }
    
return (TEntity) obj2;
}

 

这里,EF首先调用了DetectChanges()来同步内部ObjectContext跟踪的对象的状态。WrappedEntityKey则对过去的EntityKey做了一个包装,每个主键对应于一个KeyValuePair。将Find函数的输入值一一包装成KeyValuePair,并最终生成EntityKey对象。

 

之后,Find函数调用了FindInStateManager函数。顾名思义,EF首先在内部缓存中查找对应实体。对于状态是Unchanged、Modified以及Deleted的实体,FindInStateManager直接使用了过去ObjectContext.ObjectStateManager.TryGetObjectStateEntry方法,并且直接将EntityKey传给该方法。对于状态是Added的新增实体,这里需要做的处理稍微复杂一些。EF先调用了ObjectStateManager.GetObjectStateEntries(EntityState.Added)方法得到所有状态是Added的Entry。然后依次比较主键的值(之前得到的KeyValuePair)以判定是否为所要的实体。在得到Added状态的实体的值时,EF也使用了ObjectStateEntry.CurrentValues

 

如果在内部缓存中找不到相应的实体,Find函数会调用FindInStore函数来对数据库进行查找。FindInStore函数内部使用了Entity SQL的查询,以这样一个Entity SQL命令为基础:SELECT VALUE X FROM {0} AS X WHERE。再将对应的主键名字拼接到这个命令中。主键值则以ObjectParameter的形式传递。最后FindInStore调用ObjectContext.CreateQuery<>(...).SingleOrDefault()函数来执行Entity SQL查询。由于最后使用了SingleOrDefault,如果数据库中也没有对应的实体存在,Find函数会返回NULL。

 

终于一口气写完,希望对您学习EF有所帮助吧!


系列博文之二: Entity Framework 4.1 DbContext使用记之二——DbSet.Local属性的使用与实现

系列博文之三: Entity Framework 4.1 DbContext使用记之三——如何玩转实体的属性值?

 

PS:同事开发了一个很cool的MSDN论坛桌面小工具,绝对给力!欢迎使用!(我也出了不少力啊


也欢迎到MSDN中文论坛ADO.NET与LINQ论坛来提问EF的问题啊,可以试试直接报我的名字Michael Sun,哈哈! 

 

如需转发请注明原文出处,谢谢: http://www.cnblogs.com/LingzhiSun/archive/2011/03/22/EF41_Find.html

posted @ 2011-03-22 17:49  LingzhiSun  阅读(32513)  评论(27编辑  收藏  举报