Entity Framework - включает несколько уровней свойств
Метод 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; }
}
10 ответов
Для 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);
Если я вас правильно понимаю, вы спрашиваете о включении вложенных свойств. Если так:
.Include(x => x.ApplicationsWithOverrideGroup.NestedProp)
или же
.Include("ApplicationsWithOverrideGroup.NestedProp")
или же
.Include($"{nameof(ApplicationsWithOverrideGroup)}.{nameof(NestedProp)}")
EF Core: использование "ThenInclude" для загрузки нескольких уровней: например:
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ThenInclude(author => author.Photo)
.ToList();
Дополнительные примеры 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
может значительно ускорить процесс, если вы не собираетесь редактировать сущности и сохранять их заново.
Я сделал небольшой помощник для 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.
Я также должен был использовать несколько включений, и на 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();
Это может кому-то помочь:)
Позвольте мне четко заявить, что вы можете использовать строковую перегрузку для включения вложенных уровней независимо от кратности соответствующих отношений, если вы не возражаете против использования строковых литералов:
query.Include("Collection.Property")
Я придумал самый простой способ. Вам не нужно устанавливать пакет ThenInclude.EF или использовать ThenInclude для всех вложенных свойств навигации. Просто сделайте так, как показано ниже, EF позаботится обо всем остальном. пример:
var thenInclude = context.One.Include(x => x.Twoes.Threes.Fours.Fives.Sixes)
.Include(x=> x.Other)
.ToList();
Я собираюсь добавить свое решение к моей конкретной проблеме. Мне нужно было включить две коллекции одного уровня. Окончательное решение выглядело так.
var recipe = _bartendoContext.Recipes
.Include(r => r.Ingredients)
.ThenInclude(r => r.Ingredient)
.Include(r => r.Ingredients)
.ThenInclude(r => r.MeasurementQuantity)
.FirstOrDefault(r => r.Id == recipeId);
if (recipe?.Ingredients == null) return 0m;
var abv = recipe.Ingredients.Sum(ingredient => ingredient.Ingredient.AlcoholByVolume * ingredient.MeasurementQuantity.Quantity);
return abv;
Это расчет процентного содержания алкоголя в данном рецепте напитка. Как вы можете видеть, я просто дважды включил сборник ингредиентов, а затем добавил туда ингредиент и количество.
У меня есть страница индекса, на которой отображается MbsNavigation.Name, представляющий собой объект микропрограммы, загруженный как внешний ключ. Объект Firmware большой, поэтому загрузка страницы Index через Интернет занимает несколько минут.
BatterySystem = await _context.BatterySystems.Include(b => b.MbsNavigation)
Это решение для загрузки только Firmware.Name:
BatterySystem = await _context.BatterySystems
.Include(b => b.MbsNavigation)
.Select(b => new BatterySystem()
{
Name = b.Name,
MbsNavigation = new Firmware() { Name = b.MbsNavigation.Name },
})
.ToListAsync();
Теперь индекс загружается сразу.