Запит запиту linq фреймворка Включає () кілька дочірніх об'єктів


176

Це може бути справді елементарним питанням, але який приємний спосіб включити декілька дочірніх організацій під час написання запиту, який охоплює ТРИ рівні (або більше)?

тобто у мене є 4 таблиці: Company, Employee, Employee_CarіEmployee_Country

Компанія має стосунки 1: м із працівником.

Співробітник має 1: m стосунки як з Employee_Car, так і з Employee_Country.

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

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

Має бути більш елегантний спосіб! Це довго звивається і генерує жахливий SQL

Я використовую EF4 з VS 2010

Відповіді:


201

Використовуйте методи розширення . Замініть 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);

Але я хотів би використати це так: //inside public static class Extensions public static IQueryable<Company> CompleteCompanies(this DbSet<Company> table){ return table .Include("Employee.Employee_Car") .Include("Employee.Employee_Country") ; } //code will be... Company company = context.Companies.CompleteCompanies().FirstOrDefault(c => c.Id == companyID); //same for next advanced method
Хамід

Bullsye Nix. Розширення повинні бути першим портом виклику для ... ну ... розширення наперед визначених функціональних можливостей.
Приходьте

12
Роками пізніше я не рекомендував би включати в себе рядки, оскільки вони не є безпечними для виконання. Якщо ім'я властивості навігації колись зміниться або буде неправильно написано, воно порушиться. Наполегливо рекомендуємо використовувати набраний включати замість цього.
Джефф Пуц

2
з моменту введення nameof (class) можна безпечно використовувати цей підхід. Якщо ім'я сутності зміниться, воно буде вибране під час компіляції. Приклад: context.Companies.Include (nameof (Employee)) У разі, якщо вам потрібно піти далі, імена повинні відповідати nameof (Employee) + "." + Nameof (Employee_Car)
Карл

Методика методу розширення не працює для складених запитів (принаймні, не на EFCore), підтверджених тут: github.com/aspnet/EntityFrameworkCore/isissue/7016
Dunge

156

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для збереження вашої здоровості.

Інформація про застаріле (не робіть цього):

Завантаження декількох онуків може бути виконано за один крок, але для цього потрібне досить незграбне зворотне повернення графіку перед тим, як рухатись до наступного вузла (Примітка. Це НЕ працює з 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);

Тож я б залишився з першим варіантом (один Включити на модель глибини сутності листів).


4
Мені було цікаво, як це зробити із сильно набраними. Включити заяви. Проектування дітей із Select було відповіддю!

1
Мій еквівалент "co.E Employees.Select (...)" показує синтаксичну помилку на "Вибрати", кажучи, що "" Співробітники "не містять визначення для" Вибрати "[або методу розширення]". Я включив System.Data.Entity. Я хочу лише отримати один стовпчик із об’єднаної таблиці.
Кріс Уолш

1
У мене була батьківська таблиця, яка двічі посилалася на одну і ту ж дочірню таблицю. Із синтаксисом старого рядка, що включає рядок, було важко перезавантажити правильні стосунки. Цей спосіб набагато конкретніший. Будь ласка, майте на увазі, щоб включити простір імен System.Data.Entity для сильно набраних включати.
Карл

1
З ядром .net 2.1 мені потрібен був простір імен Microsoft.EntityFrameworkCore замість System.Data.Entity
denvercoder9

27

Ви можете знайти цю цікаву статтю, яка доступна на сайті codeplex.com .

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

Більше того, стаття містить ретельне порівняння продуктивності цього нового підходу із запитами EF. Цей аналіз показує, що GBQ швидко перевершує EF-запити.


як це можна реалізувати в реальному додатку?
Victor.Uduak

4

Як побудувати запит LINQ до Entities, щоб безпосередньо завантажити дочірні об’єкти, замість того, щоб викликати властивість Reference або Load ()

Не існує іншого способу - крім здійснення ледачого завантаження.

Або завантаження вручну ....

myobj = context.MyObjects.First();
myobj.ChildA.Load();
myobj.ChildB.Load();
...
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.