Entity Framework - 预加载过滤器?
Entity Framework - Eager loading with filter?
我正在使用一个数据库架构,其中记录在更新时不会被覆盖。而是添加记录的新副本并标记为 "current"。
例如:
Id | Current | Name | Owner
1 | false | Foo | Bar
1 | false | Foo | Bazz
1 | true | Foo | Buzz
在我的模型中,我有一个 Blog
实体,它有很多 Post
与之相关。每个Post
都有很多Comment
与之相关:
public class Blog
{
public int Id {get; set};
public bool Current {get; set};
public ICollection<Post> Posts {get; set;}
}
public class Post
{
public int Id {get; set};
public bool Current {get; set};
public ICollection<Comment> Comments {get; set;}
}
public class Comment
{
public int Id {get; set};
public bool Current {get; set};
}
一样急切地加载一个 Blog
及其所有 Post
和所有 Comment
using (var context = new BloggingContext())
{
// Load all blogs, all related posts, and all related comments
var blogs1 = context.Blogs
.Include(b => b.Posts.Select(p => p.Comments))
.ToList();
}
但是,我只想包含 Current == true
处的数据库记录。我如何使用 LINQ-to-EF 执行此操作?理想情况下,条件将进入 JOIN
的 ON
子句 - 这可能吗?
当前不支持使用 .Include() 预先加载时进行过滤 "out of the box" Entity Framework。您可以 vote in favor of this feature here 并希望它能够切入 EF7。
我在一个名为 EntityFramework.Include 的开源库中找到了我的问题的部分答案,它在预先加载时提供了一些过滤功能。
不幸的是,我只能将它用于层次结构的两个(三层中的)层级,如下所示:
using (var context = new BloggingContext())
{
// Load all blogs and all related posts that are "Current"
var query = context.Blogs
.Where(b => b.Current)
.Include(b => b.Posts, b => b.Posts.Where(p => p.Current).ToList());
var list = query.ToListWithInclude();
}
仍在尝试弄清楚如何更深入 Comment
s。
免责声明:我是项目的所有者Entity Framework Plus
EF+ Query IncludeFilter 允许轻松过滤包含的实体。
using (var context = new BloggingContext())
{
// Load all blogs, all related posts, and all related comments
var blogs1 = context.Blogs
.IncludeFilter(b => b.Posts.Where(x => x.Current))
.IncludeFilter(b => b.Posts.Where(x => x.Current).Select(p => p.Comments.Where(x => x.Current))
.ToList();
}
注意:由于具有导航属性的库的某些限制,必须包含每个路径。
回答sub-question
One concern: The SQL emitted is very large.
SQL是由Entity Framework生成的。 SQL 由于它们如何处理投影中的关系和包含方法,因此非常大。我们的图书馆不会生成这个 SQL.
您可以将使用EF+ Query IncludeOptimized生成的大SQL更改为执行多条语句。使用多个语句通常可以提高性能。
示例:
using (var context = new BloggingContext())
{
// Load all blogs, all related posts, and all related comments
var blogs1 = context.Blogs
.IncludeOptimized(b => b.Posts.Where(x => x.Current))
.IncludeOptimized(b => b.Posts.Where(x => x.Current).Select(p => p.Comments.Where(x => x.Current))
.ToList();
}
注意:由于具有导航属性的库的某些限制,必须包含每个路径。
根据 this Whosebug answer.
使用 "out of the box" Entity Framework 找到了解决方案
关键概念是向每个实体添加父级 属性,然后从层次结构的最低级别到最高级别 "backwards":
var query = context.Comments
.Include("Post.Blog")
.Where(comment =>
comment.Current &&
comment.Post.Current &&
comment.Post.Blog.Current )
.Select(comment => comment.Post.Blog)
.ToList();
一个重要的警告,在对该 SO 答案的评论中提到:
... if parents exist that don't have any children that match the filters, those parents won't be in the result set.
我正在使用一个数据库架构,其中记录在更新时不会被覆盖。而是添加记录的新副本并标记为 "current"。
例如:
Id | Current | Name | Owner
1 | false | Foo | Bar
1 | false | Foo | Bazz
1 | true | Foo | Buzz
在我的模型中,我有一个 Blog
实体,它有很多 Post
与之相关。每个Post
都有很多Comment
与之相关:
public class Blog
{
public int Id {get; set};
public bool Current {get; set};
public ICollection<Post> Posts {get; set;}
}
public class Post
{
public int Id {get; set};
public bool Current {get; set};
public ICollection<Comment> Comments {get; set;}
}
public class Comment
{
public int Id {get; set};
public bool Current {get; set};
}
一样急切地加载一个 Blog
及其所有 Post
和所有 Comment
using (var context = new BloggingContext()) { // Load all blogs, all related posts, and all related comments var blogs1 = context.Blogs .Include(b => b.Posts.Select(p => p.Comments)) .ToList(); }
但是,我只想包含 Current == true
处的数据库记录。我如何使用 LINQ-to-EF 执行此操作?理想情况下,条件将进入 JOIN
的 ON
子句 - 这可能吗?
当前不支持使用 .Include() 预先加载时进行过滤 "out of the box" Entity Framework。您可以 vote in favor of this feature here 并希望它能够切入 EF7。
我在一个名为 EntityFramework.Include 的开源库中找到了我的问题的部分答案,它在预先加载时提供了一些过滤功能。
不幸的是,我只能将它用于层次结构的两个(三层中的)层级,如下所示:
using (var context = new BloggingContext())
{
// Load all blogs and all related posts that are "Current"
var query = context.Blogs
.Where(b => b.Current)
.Include(b => b.Posts, b => b.Posts.Where(p => p.Current).ToList());
var list = query.ToListWithInclude();
}
仍在尝试弄清楚如何更深入 Comment
s。
免责声明:我是项目的所有者Entity Framework Plus
EF+ Query IncludeFilter 允许轻松过滤包含的实体。
using (var context = new BloggingContext())
{
// Load all blogs, all related posts, and all related comments
var blogs1 = context.Blogs
.IncludeFilter(b => b.Posts.Where(x => x.Current))
.IncludeFilter(b => b.Posts.Where(x => x.Current).Select(p => p.Comments.Where(x => x.Current))
.ToList();
}
注意:由于具有导航属性的库的某些限制,必须包含每个路径。
回答sub-question
One concern: The SQL emitted is very large.
SQL是由Entity Framework生成的。 SQL 由于它们如何处理投影中的关系和包含方法,因此非常大。我们的图书馆不会生成这个 SQL.
您可以将使用EF+ Query IncludeOptimized生成的大SQL更改为执行多条语句。使用多个语句通常可以提高性能。
示例:
using (var context = new BloggingContext())
{
// Load all blogs, all related posts, and all related comments
var blogs1 = context.Blogs
.IncludeOptimized(b => b.Posts.Where(x => x.Current))
.IncludeOptimized(b => b.Posts.Where(x => x.Current).Select(p => p.Comments.Where(x => x.Current))
.ToList();
}
注意:由于具有导航属性的库的某些限制,必须包含每个路径。
根据 this Whosebug answer.
使用 "out of the box" Entity Framework 找到了解决方案关键概念是向每个实体添加父级 属性,然后从层次结构的最低级别到最高级别 "backwards":
var query = context.Comments
.Include("Post.Blog")
.Where(comment =>
comment.Current &&
comment.Post.Current &&
comment.Post.Blog.Current )
.Select(comment => comment.Post.Blog)
.ToList();
一个重要的警告,在对该 SO 答案的评论中提到:
... if parents exist that don't have any children that match the filters, those parents won't be in the result set.