Entity Framework linq query Включает() несколько дочерних объектов

Это может быть действительно элементарный вопрос, но как хороший способ включить несколько дочерних объектов при написании запроса, который охватывает три уровня (или более)?

то есть. У меня есть 4 таблицы: Company, Employee, Employee_Car и Employee_Country

Компания имеет отношения 1: m с сотрудником.

Сотрудник имеет отношения 1: m как с Employee_Car, так и с Employee_Country.

Если я хочу написать запрос, который возвращает данные из всех 4 таблиц, в настоящее время я пишу:

Company company = context.Companies
                         .Include("Employee.Employee_Car")
                         .Include("Employee.Employee_Country")
                         .FirstOrDefault(c => c.Id == companyID);

Должен быть более элегантный способ! Это длинный winded и генерирует ужасающий SQL

Я использую EF4 с VS 2010

Ответ 1

Используйте методы расширения. Замените NameOfContext именем вашего контекста объекта.

public static class Extensions{
   public static IQueryable<Company> CompleteCompanies(this NameOfContext context){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country") ;
     }

     public static Company CompanyById(this NameOfContext context, int companyID){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country")
             .FirstOrDefault(c => c.Id == companyID) ;
      }

}

Затем ваш код станет

     Company company = 
          context.CompleteCompanies().FirstOrDefault(c => c.Id == companyID);

     //or if you want even more
     Company company = 
          context.CompanyById(companyID);

Ответ 2

EF 4.1 до EF 6

Существует строго типизированный .Include, который позволяет указать требуемую глубину требуемой загрузки, предоставляя выбор выражений на соответствующую глубину:

using System.Data.Entity; // NB!

var company = context.Companies
                     .Include(co => co.Employees.Select(emp => emp.Employee_Car))
                     .Include(co => co.Employees.Select(emp => emp.Employee_Country))
                     .FirstOrDefault(co => co.companyID == companyID);

Sql, сгенерированный в обоих случаях, по-прежнему никоим образом не интуитивно понятен, но кажется достаточно показательным. Я привел небольшой пример в GitHub здесь

EF Core

EF Core имеет новый метод расширения .ThenInclude(), хотя синтаксис немного отличается:

var company = context.Companies
                     .Include(co => co.Employees)
                           .ThenInclude(emp => emp.Employee_Car)
                      ...

Как и в документах, я бы сохранил дополнительный "отступ" в .ThenInclude, чтобы сохранить ваше здравомыслие.

Устаревшая информация (не делайте этого):

Загрузка нескольких внуков может быть выполнена за один шаг, но для этого требуется довольно неудобное обращение назад вверх по графику, прежде чем перейти к следующему node (NB: Это НЕ работает с AsNoTracking() - вы получите ошибка времени выполнения):

var company = context.Companies
         .Include(co => 
             co.Employees
                .Select(emp => emp.Employee_Car
                    .Select(ec => ec.Employee)
                    .Select(emp2 => emp2.Employee_Country)))
         .FirstOrDefault(co => co.companyID == companyID);

Таким образом, я остался бы с первым вариантом (один пример для каждой модели сущности сущности листа).

Ответ 3

Вы можете найти эту статью, которая доступна по адресу codeplex.com.

В статье представлен новый способ выражения запросов, которые охватывают несколько таблиц в форме декларативных форм графа.

Кроме того, статья содержит тщательное сравнение производительности этого нового подхода с запросами EF. Этот анализ показывает, что GBQ быстро превосходит запросы EF.