Entity Framework загружает дочернюю коллекцию с порядком сортировки
У меня две таблицы: родительская и дочерняя. В дочерней таблице есть порядок сортировки столбцов (числовое значение). Из-за отсутствующей поддержки EF для продолжения IList включите порядок сортировки, не выставляя порядок сортировки (см. Сущностная структура, сохраняющая порядок сортировки дочерней коллекции). Мой дочерний класс также свойство SortOrder, чтобы я мог хранить дочерние элементы с порядком сортировки.
В отличие от автора заданного вопроса, я пытаюсь загрузить детей, которые всегда сортируются. Поэтому, если я загружаю родительский экземпляр, я ожидаю, что дочерняя коллекция сортируется по порядку сортировки. Как я могу достичь такого поведения с API первого кода API и POCO?
Подсказка: это не возможность вызывать .Sort(...) в дочерней коллекции.
Ответы
Ответ 1
Вы не можете добиться этого напрямую, потому что ни жадная, ни ленивая загрузка в EF не поддерживает упорядочение или фильтрацию.
Ваши варианты:
- Сортировка данных в приложении после загрузки из базы данных
- Выполните отдельный запрос для загрузки дочерних записей. После использования отдельного запроса вы можете использовать
OrderBy
Вторая опция может использоваться с явной загрузкой:
var parent = context.Parents.First(...);
var entry = context.Entry(parent);
entry.Collection(e => e.Children)
.Query()
.OrderBy(c => c.SortOrder)
.Load();
Ответ 2
Вы можете сделать это эффективно в одном запросе, грамматика просто неудобна:
var groups = await db.Parents
.Where(p => p.Id == id)
.Select(p => new
{
P = p,
C = p.Children.OrderBy(c => c.SortIndex)
})
.ToArrayAsync();
// Query/db interaction is over, now grab what we wanted from what was fetched
var model = groups
.Select(g => g.P)
.FirstOrDefault();
Объяснение
асинхронная заметка
Мне пришлось использовать расширения async
здесь, которые вы, вероятно, должны использовать, но вы можете избавиться от await
/async
, если вам нужен синхронный запрос без ущерба для эффективной сортировки детей.
Первый фрагмент
По умолчанию все объекты EF, извлеченные из Db, "отслеживаются". Кроме того, EF, эквивалентный SQL Select
, предназначен для анонимных объектов, которые вы видите в нашем примере. Когда создается анонимный объект, объекты, назначенные P
и C
, отслеживаются, что означает, что их отношения отмечены, а их состояние поддерживается EF Change Tracker. Поскольку C
представляет собой список дочерних элементов в P
, даже если вы не запрашивали, чтобы они были явно указаны в вашем анонимном объекте, EF загружает их как эту дочернюю коллекцию в любом случае из-за отношения, которое она видит в схеме.
Чтобы узнать больше, вы можете разбить это на 2 отдельных запроса, загружая только родительский объект, а затем только дочерний список, в совершенно разные вызовы Db. EF Change Tracker будет замечать и загружать детей в родительский объект для вас.
Второй фрагмент
Мы обманули EF, чтобы вернуть упорядоченных детей. Теперь мы захватим только объект "Родитель" - его дети все равно будут привязаны по порядку, как мы хотели.
Нули и таблицы как наборы
Здесь есть неудобный 2-шаг, в основном для лучших практик вокруг нулей; он должен сделать 2 вещи:
-
Подумайте о вещах в db, которые устанавливаются до тех пор, пока не будет достигнут абсолютный последний момент.
-
Избегайте нулевых исключений.
Другими словами, последний фрагмент мог быть:
var model = groups.First().P;
Но если объект не присутствовал в db, он будет взорваться с исключением нулевой ссылки. С# 6 представит еще одну альтернативу, но оператор объединения свойств null - так что в будущем вы могли бы заменить последний кусок:
var model = groups.FirstOrDefault()?.P;