Entity Framework - включает несколько уровней свойств

376

Метод Include () хорошо работает для списков объектов. Но что, если мне нужно пройти два уровня? Например, метод ниже вернет ApplicationServers с включенными свойствами, показанными здесь. Однако ApplicationsWithOverrideGroup - это другой контейнер, который содержит другие сложные объекты. Могу ли я сделать функцию Include () для этого свойства? Или как я могу получить это свойство для полной загрузки?

В нынешнем виде этот метод:

public IEnumerable<ApplicationServer> GetAll()
{
    return this.Database.ApplicationServers
        .Include(x => x.ApplicationsWithOverrideGroup)                
        .Include(x => x.ApplicationWithGroupToForceInstallList)
        .Include(x => x.CustomVariableGroups)                
        .ToList();
}

Заполняет только свойство Enabled (ниже), но не свойства Application или CustomVariableGroup (ниже). Как мне это сделать?

public class ApplicationWithOverrideVariableGroup : EntityBase
{
    public bool Enabled { get; set; }
    public Application Application { get; set; }
    public CustomVariableGroup CustomVariableGroup { get; set; }
}
Боб Хорн
источник
Привет, Почему я получаю исключение Expression must be a member expressionпри попытке это: Для того, чтобы включать в себя коллекцию , а затем уровень сбора один вниз: query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection)).
Joe.wang
1
@BobHorn, у меня та же проблема. В моем случае, вложенность идет глубоко в несколько слоев, мне удалось сделать то, что вы указали. В сгенерированном SQL я мог видеть, что все столбцы возвращаются с разными псевдонимами, как c1, c2 и так далее. Мой вопрос в том, как я могу сформировать вложенную коллекцию DTO из всех моих включений :(. Может быть, вы можете взять сам пример, приведенный выше, в том смысле, что мы возвращаем все столбцы без какого-либо пользовательского DTO (который сам по себе является коллекцией DTO )
TechQuery

Ответы:

705

Для EF 6

using System.Data.Entity;

query.Include(x => x.Collection.Select(y => y.Property))

Не забудьте добавить, using System.Data.Entity;чтобы получить версию, Includeкоторая принимает в лямбду.


Для EF Core

Используйте новый метод ThenInclude

query.Include(x => x.Collection)
     .ThenInclude(x => x.Property);
Диего Торрес
источник
1
Я не могу сделать Include () для ApplicationsWithOverrideGroup. Это не проявляется в intellisense.
Боб Хорн
Я не могу использовать ваши правки, потому что ApplicationsWithOverrideGroup - это список. Приложение - это свойство каждого элемента в списке, а не самого списка.
Боб Хорн
1
Аааа, но та ссылка, которую вы предоставили, кажется, дает ответ. Позвольте мне попробовать это: включить коллекцию, а затем коллекцию на один уровень ниже: query.Include (e => e.Level1Collection.Select (l1 => l1.Level2Collection)).
Боб Хорн
60
Не забудьте включить System.Data.Entity в употребления. В противном случае Intellisense предоставит вам только версию метода (строка пути).
О.Дж. Ракеньо
5
@ Похоже, вам нужно позвонить Includeдля каждого свойства:Db.States.Include(state => state.Cities.Select(city => city.Customers).Include(state => state.Cities.Select(city => city.Vendors)
Диего Торрес
72

Если я вас правильно понимаю, вы спрашиваете о включении вложенных свойств. Если так :

.Include(x => x.ApplicationsWithOverrideGroup.NestedProp)

или

.Include("ApplicationsWithOverrideGroup.NestedProp")  

или

.Include($"{nameof(ApplicationsWithOverrideGroup)}.{nameof(NestedProp)}")  
Дзюдо
источник
6
Спасибо, я могу попробовать это. Я надеялся, что смогу держать вещи строго напечатанными и избегать строковых литералов. Но если это то, как это должно быть сделано ...
Боб Хорн
1
Вы были близки Я, возможно, не ясно, что ApplicationsWithOverrideGroup был список. Спасибо за помощь!
Боб Хорн
@Judo, у меня та же проблема .. В моем случае, вложенность идет глубоко в несколько слоев, мне удалось сделать включение, которое вы указали. В сгенерированном SQL я мог видеть, что все столбцы возвращаются с разными псевдонимами, как c1, c2 и так далее. Мой вопрос в том, как я могу сформировать вложенную коллекцию DTO из всех моих включений :(. Может быть, вы можете взять сам пример, приведенный выше, в том смысле, что мы возвращаем все столбцы без какого-либо пользовательского DTO (который сам по себе является коллекцией DTO )
TechQuery
2
Не забудьте включить System.Data.Entity в употребления. В противном случае Intellisense предоставит вам только Include(string path)версию метода.
AlexMelw
53

EF Core: использование «ThenInclude» для загрузки нескольких уровней: например:

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
    .ToList();
thangcao
источник
53
Похоже, это только EF Core
Крис Марисик
27
К вашему сведению: VS2017 Intellisense не работал для .ThenInclude. Просто введите его так, как вы думаете, и подсветка ошибок исчезнет.
JohnWrensby
4
Я хочу подчеркнуть комментарий @JohnWrensby, иногда Intellisense может занять особенно много времени для обработки ThenInclude, это может быть довольно запутанным для новых пользователей. У меня также были случаи, когда простое лямбда-выражение «Включить» не обрабатывалось должным образом, пока вы просто не набрали его и не скомпилировали, игнорируя «ошибки», показанные в VS.
Pac0
@ Pac0 ты спас мой день. изо всех сил, чтобы увидеть дочерние элементы и не мог.
Бендрам
28

Я сделал небольшой помощник для Entity Framework 6 (стиль .Net Core), чтобы красиво включать под-сущности.

Сейчас на NuGet: Install-Package ThenInclude.EF6

using System.Data.Entity;

var thenInclude = context.One.Include(x => x.Twoes)
    .ThenInclude(x=> x.Threes)
    .ThenInclude(x=> x.Fours)
    .ThenInclude(x=> x.Fives)
    .ThenInclude(x => x.Sixes)
    .Include(x=> x.Other)
    .ToList();

Пакет доступен на GitHub .

Lenny32
источник
привет, у меня есть исключение во время выполнения, я не могу привести IncludableQueryable <observablecollection> к IncludableQueryable <genericcollection>
user2475096
Сначала я использую db, и я изменил файл tt, чтобы получить ObservableCollections для всех моих сущностей, любая помощь приветствуется.
user2475096
2
@ lenny32 что-нибудь знать об этом расширении?
Аарон Худон
Обратите внимание, что это не требуется, если свойство, к которому вы переходите, является взаимно-однозначным с DbSet, к которому вы переходили, и вы можете объединиться DbSet<One>().Include(x => x.Two.Three.Four.Five.Six)с единственным недостатком, заключающимся в том, что вы вычисляете декартово произведение и потенциально увеличиваете пропускную способность.
Джон Заброски
23

Другие примеры EFCore на MSDN показывают, что вы можете делать довольно сложные вещи с Includeи ThenInclude.

Это хороший пример того, насколько сложным вы можете стать (это все одно утверждение!):

viewModel.Instructors = await _context.Instructors

      .Include(i => i.OfficeAssignment)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Enrollments)
                .ThenInclude(i => i.Student)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Department)

      .AsNoTracking()
      .OrderBy(i => i.LastName)
      .ToListAsync();

Посмотрите, как вы можете цепляться Includeдаже после, ThenIncludeи это как бы «сбрасывает» вас обратно на уровень сущности верхнего уровня (Инструкторы).

Вы даже можете повторять одну и ту же коллекцию «первого уровня» (CourseAssignments) несколько раз, а затем использовать отдельные ThenIncludesкоманды для доступа к различным дочерним объектам.

Обратите внимание, что ваш фактический запрос должен быть помечен в конце цепочки Includeили ThenIncludes. Следующее НЕ работает:

var query = _context.Instructors.AsQueryable();
query.Include(i => i.OfficeAssignment);

var first10Instructors = query.Take(10).ToArray();

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

AsNoTracking может значительно ускорить процесс, если вы не собираетесь редактировать сущности и сохранять их заново.

Simon_Weaver
источник
Есть ли способ получить и зачисление, и зачисление в департаменты без повторного. Включая назначение курса и курса? (До сих пор кажется, что Api может пойти глубже с .ThenInclude или вернуться на верхний уровень с .Include, но нечего оставаться на том же уровне?)
Уильям Джокуш,
Если вы хотите ленивой загрузки, следите за обновлениями для EF Core 2.1 blogs.msdn.microsoft.com/dotnet/2018/02/02/… но если вы просто хотите загружать больше на том же уровне, я думаю, что это сделано специально . Я не уверен, что вы думаете - для этого не нужно много лишнего, и это значительно уменьшает то, что возвращается из базы данных. У сущности может быть только одна или две вещи «одного уровня», но также может быть 50 для большого проекта, поскольку явное явление делает ваше приложение намного быстрее.
Simon_Weaver
Это было хорошим объяснением концепции Включить «сброс» уровня обратно на начальный уровень снова. Помогает мне обернуть голову вокруг иерархии включенной системы. Ура!
AFM-Horizon
22

Я также должен был использовать несколько включений, и на 3-м уровне мне нужно было несколько свойств

(from e in context.JobCategorySet
                      where e.Id == id &&
                            e.AgencyId == agencyId
                      select e)
                      .Include(x => x.JobCategorySkillDetails)
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.DurationType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RuleType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RateType))
                      .FirstOrDefaultAsync();

Это может кому-то помочь :)

dnxit
источник
1
можно ли это сделать без повторения.Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt......
Multinerd
ну, это зависит от того, как глубоко ты хочешь пойти
dnxit
7

Позвольте мне четко заявить, что вы можете использовать строковую перегрузку для включения вложенных уровней независимо от кратности соответствующих отношений, если вы не возражаете против использования строковых литералов:

query.Include("Collection.Property")
mrmashal
источник
1
Этот метод был полезен для меня, чтобы выяснить, как это может быть закодировано в VB, поскольку я не могу найти где-нибудь после часов поиска в Google.
Кодер
Это прекрасно работает для меня, я использую это много! Это даже работает в сочетании с заявлениями .SelectMany:query.SelectMany(x=>x.foos).Include("bar").Include("bar.docs")...
Эфи