Entity Framework 小技巧三 —— 如何在导入集合类型的Navigation Property时增加过滤条件?
在使用EF的集合类型的Navigation Property时,我时常需要只导入符合特定条件的实体对象,例如:Blog类和Post类存在一对多关系,即一个博客可以拥有多篇博文。现在我们想获得博客以及其相关博文中在今年1月1日之后发表的所有博文,可是之前EF版本所提供的Lazy Loading、Explicitly Loading和Eagerly Loading都不支持在Navigation Property上增加一个过滤条件。有关三种Loading的详细信息,不在本文过多介绍,请见MSDN文档:http://msdn.microsoft.com/en-us/library/bb896272.aspx,或者参看我以前写过的一篇英文MSDN博客:http://blogs.msdn.com/b/msdnforum/archive/2010/07/12/community-goodies-lazy-loading-in-entity-framework-4.aspx。之后也会为大家带来有关Loading的文章。
过去我们可以用以下两种方法来实现这一目的:
1) 利用匿名类,并且将过滤条件嵌入到返回的匿名类中:
{
var query = (from b in context.Blogs
where b.Tags.Contains("EF")
select new
{
Blog = b,
Posts = b.Posts.Where(p => p.CreationTime >= new DateTime(2011, 1, 1))
}).ToList();
}
不过,此方法的缺点显而易见, 我们必须使用匿名类来包装实体对象,在数据绑定时会有挺多问题。
2) 使用多个query,让EF的跟踪机制帮助我们建立对象之间的联系。(EF4和EF4.1中,我们需要关闭Lazy Loading)
{
// For EF4
// context.ContextOptions.LazyLoadingEnabled = false;
// For EF4.1
// context.Configuration.LazyLoadingEnabled = false;
var blogs = (from b in context.Blogs
where b.Tags.Contains("EF")
select b).ToList();
var posts = (from p in context.Posts
where p.Blog.Tags.Contains("EF")
&& p.CreationTime >= new DateTime(2011, 1, 1)
select p).ToList();
}
在两个query执行之后,EF的ObjectStateManager会自动建立Blog和Post之间的关系,如果我们访问blogs.First().Posts属性,将得到的都是今年1月1日之后创建的博文。
当然我想这两个方法在EF4.1中同样能成功。幸运的是,EF4.1提供了新的功能使我们可以在使用Explicitly Loading时添加过滤条件。
{
var blog = context.Blogs.Where(b => b.Tags.Contains("EF")).First();
context.Entry(blog).Collection(b => b.Posts)
.Query()
.Where(p => p.CreationTime >= new DateTime(2011, 1, 1))
.Load();
// 或者使用String类型来标记Blog与Post的关系
context.Entry(blog).Collection("Posts")
.Query().Cast<Post>()
.Where(p => p.CreationTime >= new DateTime(2011, 1, 1))
.Load();
}
Collection()方法在这里返回DbCollectionEntry,Query()方法返回对应的IQueryable<T>对象。熟悉LINQ的同志们一定知道,得到IQueryable<T>了的话,一切好说,哈哈。EF4.1中,System.Data.Entity命名空间下的DbExtensions类中定义了不少LINQ的扩展方法,其中一个就是Load方法。用Reflector看了下Load方法的实现,其实就是调用IEnumerator.MoveNext遍历一遍集合:
{
while (enumerator.MoveNext())
{
}
}
当然这里如果我们不想导入这些相关的实体对象,也可以使用Count()方法得到符合条件对象的数量。这样我们得到的是实体的数量,而没有一个对应实体会被导入。
{
var blog = context.Blogs.Where(b => b.Tags.Contains("EF")).First();
context.Entry(blog).Collection(b => b.Posts)
.Query()
.Count();
}
一口气搞定这篇,希望对大家学习EF有所帮助吧!
PS1:这里为大家带来一个好消息:微软一站式实例代码库(Microsoft All-In-One Code Framework)即日起正式迁移至MSDN代码库了,新的平台会帮您更轻松地解决开发难题、节省更多时间、获得更友好的用户体验。本人作为这个项目的元老,见到我们已拥有600多个经典的代码实例,甚感欣慰啊! 更详细信息,请看http://msdn.microsoft.com/zh-cn/hh124104.aspx?ocid=ban-f-cn-loc-OC201104-MSDN
最新的代码浏览器也发布啦!为大家带来了更多很cool的功能,比如1)代码按需下载 2)实例集中化管理 3)自动更新。
http://blog.csdn.net/MSCodeSample/archive/2011/04/18/6331382.aspx
之后我将尽力为大家带来更多有关EF的代码实例以及相关的介绍!
PS2:同事开发了一个很cool的MSDN论坛桌面小工具,绝对给力!欢迎使用!(我也出了不少力啊)
也欢迎到MSDN中文论坛ADO.NET与LINQ论坛来提问EF的问题啊,可以试试直接报我的名字Michael Sun,哈哈!
如需转发请注明原文出处,谢谢:http://www.cnblogs.com/LingzhiSun/archive/2011/04/19/EF_Trick3.html