简体   繁体   English

实体框架 - 急切加载过滤器?

[英]Entity Framework - Eager loading with filter?

I'm working with a DB schema where records are not overwritten when updated. 我正在使用数据库架构,在更新时不会覆盖记录。 Rather a new copy of the record is added and marked as "current". 而是添加记录的新副本并标记为“当前”。

For example: 例如:

Id | Current | Name | Owner
1  | false   | Foo  | Bar
1  | false   | Foo  | Bazz
1  | true    | Foo  | Buzz

In my model I have a Blog entity that has many Post s related to it. 在我的模型中,我有一个Blog实体,其中包含许多与之相关的Post Each Post has many Comment s related to it: 每个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};
}

I would like to eagerly load a Blog with all its Post s and all their Comment s much like in this example from MSDN : 我想热切地加载一个Blog及其所有Post和所有Comment ,就像在MSDN的这个例子中一样:

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(); }

However, I would like to only include DB records where Current == true . 但是,我想只包含其中Current == true DB记录。 How can I do this with LINQ-to-EF? 如何使用LINQ-to-EF执行此操作? Ideally the condition would go into the JOIN 's ON clause - is this possible? 理想情况下,条件会进入JOINON子句 - 这可能吗?

Disclaimer : I'm the owner of the project Entity Framework Plus 免责声明 :我是项目Entity Framework Plus的所有者

The EF+ Query IncludeFilter allow easily filter included entities. 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(); 
}

Note: Every path must be included due to some limitations of the library with navigation properties. 注意:由于具有导航属性的库的某些限制,必须包含每个路径。

Wiki: EF+ Query Include Filter Wiki: EF + Query包含过滤器


Answer sub-question 回答子问题

One concern: The SQL emitted is very large. 一个问题:发出的SQL非常大。

The SQL is generated by Entity Framework. SQL由Entity Framework生成。 The SQL is very large due to how they handle relation in projection and include method. 由于它们如何处理投影和包含方法中的关系,SQL非常大。 Our library doesn't generate this SQL. 我们的库不生成这个SQL。

You can change the big SQL Generated by using EF+ Query IncludeOptimized to execute multiple statement instead. 您可以使用EF + Query IncludeOptimized来更改生成的大SQL以执行多个语句。 Using multiple statements often improve the performance. 使用多个语句通常可以提高性能。

Example: 例:

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(); 
}

Note: Every path must be included due to some limitations of the library with navigation properties. 注意:由于具有导航属性的库的某些限制,必须包含每个路径。

Wiki: EF+ Query IncludeOptimized Wiki: EF + Query IncludeOptimized

Found a solution using "out of the box" Entity Framework based on this StackOverflow answer . 使用基于此StackOverflow答案的 “开箱即用”实体框架找到了解决方案。

The key concept is to add a parent property to each entity and then go "backwards" from the lowest level of the hierarchy up to the top: 关键概念是向每个实体添加父属性,然后从层次结构的最低级别“向后”向上移动到顶部:

var query = context.Comments
    .Include("Post.Blog")
    .Where(comment =>
        comment.Current &&
        comment.Post.Current &&
        comment.Post.Blog.Current )
    .Select(comment => comment.Post.Blog)
    .ToList();

One important caveat that's mentioned in a comment to that SO answer: 在对SO答案的评论中提到的一个重要警告

... if parents exist that don't have any children that match the filters, those parents won't be in the result set. ...如果父母存在没有任何与过滤器匹配的子项,那么这些父母将不在结果集中。

Filtering when eager loading with .Include() is not currently supported "out of the box" with Entity Framework. 当使用实体框架“开箱即用”时,目前不支持使用.Include()进行预先加载时进行过滤。 You can vote in favor of this feature here and hopefully it makes the cut into EF7. 你可以在这里投票赞成这个功能 ,希望它可以切入EF7。

I've found a partial answer to my question in an open source library called EntityFramework.Include that provides some filtering capabilities at the time of eager loading. 我在一个名为EntityFramework.Include的开源库中找到了我的问题的部分答案,它在急切加载时提供了一些过滤功能。

Unfortunately I've only been able to use it for two (out of three) levels of my hierarchy like so: 不幸的是,我只能将它用于我的层次结构中的两个(三个)级别,如下所示:

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();
}

Still trying to figure out how to go one level deeper into Comment s. 仍然试图弄清楚如何更深入到Comment s。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM