Фильтр вложенного свойства - PullRequest
0 голосов
/ 14 июля 2020

У меня есть следующие модели:

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 ]

1 голос
/ 15 июля 2020

Сущности не фильтруют подобным образом. Сущность блога будет и должна ссылаться на ВСЕ сообщения, с которыми она связана. EF может применять глобальные фильтры к данным, чтобы учесть такие вещи, как мягкое удаление (IsActive) или сценарий аренды (ClientId) ios, но не фильтруемые дочерние элементы, подобные этому.

Это проблема представления / потребителя, а не домен один, поэтому вы должны стремиться разделить эти проблемы с помощью 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();
0 голосов
/ 14 июля 2020

Вы можете подойти к этому снизу вверх.

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 или в окне вывода в 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()
...