Reputation: 113
I have the following models:
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; }
}
I have an IQueryable like:
var results = Blog.Include(x => x.Posts);
Everything works great until I want to filter on a property of the Post class. I need something like this:
var filteredResults = results.Where(x => x.Posts.Where(y => y.Description == "Test"));
This works If I append Any() to the second .Where(). This would not be right though because I only want to return the matching Posts, not all. Any suggestions on how to approach this?
Upvotes: 1
Views: 900
Reputation: 174
You could approach this from bottom up.
var blogIds = Posts.Where(x => x.Description == "Test").Select(x => x.BlogId);
var result = Blog.Where(x => blogIds.Contains(x.Id))
Note that you might want to do:
x => x.Description.Contains("Test")
instead of:
x => x.Description == "Test"
in first query
You'll still have to map corresponding posts to each blog though
Update
Steve's answer is correct. I'll just add that it may translate in a lot of nested select queries. You can check the output in SQL Server profiler, or in the output window in Visual Sudio. So here's everything including mapping:
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()
Upvotes: 0
Reputation: 34653
Entities don't filter like this. A Blog entity will, and should refer to ALL Posts it is associated with. EF can apply global filters to data to accommodate things like soft-delete (IsActive) or tenancy (ClientId) scenarios, but not filtered children like this.
This is a view/consumer concern, not a domain one so you should be looking to separate those concerns using Projection to return the data you want:
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();
Upvotes: 1