Отфильтровать таблицу «Включает» в запросе Entity Framework

17

Это для платформы Entity Framework для .NET 3.5:

Мне нужно запросить таблицу и включить коллекцию таблицы «many» отношения «один ко многим». Я пытаюсь отфильтровать эту коллекцию как часть запроса - я довольно новичок в Entity Framework, и мне сложно понять это.

Упрощенный пример: у автора есть Книги, а в книге есть столбец IsFiction. Я хочу отфильтрованный список авторов вместе со всеми художественными книгами.

Без фильтра это легко:

var q = from a in db.Authors.Include("Books")
        where a.BirthYear > 1900
        select a;

Я могу фильтровать после факта, что-то вроде:

var fictionBooks = a.Books.Where(b => b.IsFiction);

Но проблема в том, что исходный запрос уже выполнялся и включал эти результаты, что является ненужной обработкой базы данных.

Я могу запросить отдельно, например:

var q = from a in db.Authors where a.BirthYear > 1900 select a;
foreach (var a in q)
{
    var books = from b in db.Books 
                where ((b.Author.Id == a.Id) && (b.IsFiction))
                select b;
}

Но, конечно, это один вызов для каждого автора, которого я тоже хочу избежать.

Я могу вернуться назад, например:

var allBooks = from b in db.Books.Include("Author")
               where b.IsFiction
               select b;

Но потом я возвращаюсь к исходной проблеме, за исключением теперь на стороне автора, а не на стороне книги.

Должно быть решение, которое охватывает все - я могу сделать это в SQL довольно легко:

select * from author a
left join book b on a.id = b.author_id and b.is_fiction = 1
where a.birth_year > 1900

Любые предложения?

    
задан Joe Enos 23.08.2010 в 07:02
источник
  • Голосовать за фильтрацию Включить сюда! –  Chris 27.09.2013 в 09:31

1 ответ

14

Вперед:

var q = from a in db.Authors.Include("Books")
        where a.BirthYear > 1900
        select new {
            Author = a,
            FictionBooks = a.Books.Where(b => b.IsFiction)
        };

Другой способ, полученный из SQL в нижней части вашего вопроса:

var q = from a in db.Authors
        from b in db.Books.Include("Author")
        where a.BirthYear > 1900 && b.IsFiction && a.Id == b.Author.Id
        select new { Author = a, Book = b };

Основное различие между этими двумя состоит в том, что первый из них в основном даст вам коллекцию авторов плюс список художественных книг для каждого автора (который может быть пустым); в то время как вторая даст вам коллекцию пар автор / книги (поэтому она не возвращает никаких авторов без художественных книг).

    
ответ дан Timwi 23.08.2010 в 07:08
  • Спасибо за идею. Я надеюсь на самом деле получить коллекцию объектов Author, если это возможно, а не анонимные типизированные объекты. В том же примечании, если бы я взял этот анонимно типизированный объект и перечислил коллекцию «Авторские книги», не вернул бы ли он весь сборник книг, а не только художественные? –  Joe Enos 23.08.2010 в 07:17
  • @Joe Enos: Если вы перечислили коллекцию книг автора, да, конечно. Вот почему есть свойство FictionBooks :) –  Timwi 23.08.2010 в 07:20
  • Если вы надеетесь получить объекты Author, чья коллекция книг явно неполна (потому что она отфильтрована фикцией), на самом деле это не так, как LINQ должен работать. Каждый объект Author, возвращаемый запросом LINQ, предназначен для представления объекта Author в БД, а не его измененной версии. –  Timwi 23.08.2010 в 07:24
  • @Joe Enos: Я только использовал LINQ-to-SQL, но не LINQ-to-Entities, поэтому я не знаю деталей. Разница между ними уже укусила меня сегодня. Я рекомендую вам посмотреть на SQL-запрос, фактически отправленный на сервер, если вы хотите быть уверенным, что он делает под капотом. –  Timwi 23.08.2010 в 07:31