过滤嵌套属性

时间:2020-07-14 16:06:24

标签: c# entity-framework linq

我有以下型号:

public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; }
    public List<Post> Posts { get; set; }
}

public class Post 
{
    public int Id { get; set; }
    public string Description { get; set; }
    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

我有一个IQueryable,如:

var results = Blog.Include(x => x.Posts);

在我想过滤Post类的属性之前,一切工作都很好。我需要这样的东西:

var filteredResults = results.Where(x => x.Posts.Where(y => y.Description == "Test"));

如果我将Any()附加到第二个.Where()上,则此方法有效。但这不是正确的,因为我只想返回匹配的帖子,而不是全部。 有关如何解决此问题的任何建议?

2 个答案:

答案 0 :(得分:1)

实体不这样过滤。 Blog实体将并且应该引用与其关联的 ALL 帖子。 EF可以将全局过滤器应用于数据,以适应诸如软删除(IsActive)或租户(ClientId)方案之类的事情,但不能像这样过滤子级。

这是一个视图/消费者关注点,而不是域关注点,因此您应该使用Projection返回所需的数据来分离这些关注点:

string postFilter = "Test";

var filteredResults = context.Blogs
    .Where(x => x.Posts.Any(p => p.Description == postFilter))
    .Select(x => new BlogViewModel
    {
        BlogId = x.BlogId,
        Title = x.Title,
        FilteredPosts = x.Posts.Where(p => p.Description == postFilter)
            .Select(p => new PostViewModel
            {
               PostId = p.PostId,
               Description = p.Description,
               Text = p.Text,
               // ...
           {).ToList()
    }).ToList();

答案 1 :(得分:0)

您可以自下而上地解决这个问题。

var blogIds = Posts.Where(x => x.Description == "Test").Select(x => x.BlogId);
var result = Blog.Where(x => blogIds.Contains(x.Id))

请注意,您可能想这样做:

x => x.Description.Contains("Test")

代替:

x => x.Description == "Test"

第一次查询

尽管如此,您仍然必须将相应的帖子映射到每个博客

更新

史蒂夫的答案是正确的。我只是补充说,它可能会转换为许多嵌套的选择查询。您可以在SQL Server事件探查器中或Visual Sudio的输出窗口中检查输出。因此,这里包括映射在内的所有内容:

var posts = Posts.Where(x => x.Description == "test").ToList();
var blogIds = posts.Select(x => x.BlogId).ToList();
var blogs = Blog.Where(x => blogIds.Contains(x.Id)).ToList();

foreach(var blog in blogs)
    blog.Posts = posts.Where(x => x.BlogId == x.Id).ToList()
相关问题