Вы должны понимать, для чего предполагается использовать include.
Согласно спецификациям, QueryableExtensions.Include будет включать полностью указанное свойство. Довольно часто люди используют это как сокращение для «выбрать все свойства».
Предположим, у вас есть простое отношение «один ко многим» между школами и учащимися: в каждой школе ноль или более учеников, каждый ученик посещает ровно одна школа, а именно школа, к которой относится внешний ключ.
Если в школе [110] 2000 учеников, то каждый ученик школы [10] будет иметь внешний ключ со значением 10. Если вы используете Включить чтобы получить School [10] с учениками, вы передадите это же значение 10 более 2000 раз. Какая трата вычислительной мощности!
Было бы гораздо разумнее использовать Select для выборки данных:
var schools = dbContext.Schools.Select(school => new
{
// Select only the School properties that you plan to use
Id = school.Id,
Name = school.Name,
...
Students = school.Students.Select(student => new
{
// again: only the properties that you plan to use
Id = student.Id,
Name = student.Name,
...
// not needed, you know the value
// SchoolId = student.SchoolId,
})
.ToList(),
});
В Entity framework всегда используйте Select для запроса данных и выбора только свойства, которые вы планируете использовать. Используйте include только в том случае, если вы планируете изменить извлеченные данные.
Не используйте Include, чтобы не набирать текст.
Преимущество Select заключается в том, что вы можете возвращать данные, которые отличается от ваших исходных данных, а это именно то, что вам нужно.
Вы хотите получить «несколько свойств Items, каждый элемент с перечисляемой последовательностью нескольких свойств его ItemCategories»
var result = dbContext.Items.Select(item => new
{
Id = item.Id,
Name = item.Name,
...
ItemCategories = item.ItemCategories.Select(category => new
{
Id = category.Id,
...
});
})
Обратите внимание, что ItemCategories - это IQueryable некоторого анонимного типа. Если вы хотите, чтобы это было IEnumerable<ItemCategory>
, используйте .Select(category => new ItemCategory
и заполните только те свойства, которые вы планируете использовать.
Результатом будет IQueryable<ItemCategory>
.
- Положительный результат: если вызывающий абонент хочет только несколько категорий ItemCategories, вы не получите их все.
- Отрицательный: вам придется поддерживать dbContext в активном состоянии до тех пор, пока не будут выбраны все желаемые элементы.
Проблема в том, что пользователи не будут знать, какие свойства у вас есть, а какие нет. Чтобы преодолеть это, люди склонны отделять внутреннюю структуру данных (= ваши классы в DbSet) от данных, которые вы предоставляете другим:
.Select(category => new ExternalCategory()
{
Id = category.Id,
...
});
Хорошая вещь в этом методе заключается в том, что вы можете давать разные доступ пользователей к различным значениям: некоторые пользователи получают данные только с помощью свойств получения, у других есть методы для обновления данных, а суперпользователи имеют доступ к таблицам изменений.
Также: путем отделения структуры базы данных от данных, которые другие могут доступа, вы сможете вносить небольшие изменения в базу данных, не меняя пользователей.