Как заказать дочерние коллекции сущностей в EF
У меня есть следующий запрос:
public IEnumerable<Team> GetAllTeamsWithMembers(int ownerUserId)
{
return _ctx.Teams
.Include(x => x.TeamMembers)
.Where(x => x.UserId == ownerUserId)
.OrderBy(x => x.Name).ToList();
}
Как мне упорядочить команды по их названию, а затем отсортировать всех дочерних членов каждой команды по их названию?
Кажется, что для этого мне нужно создать новый класс DTO и использовать выбор. Я хотел бы использовать уже созданные сущности EF, в этом случае у Team есть свойство навигации для участников. я возвращаюсь IEnumerable<Team>
из моего слоя хранилища.
Кажется, не существует изящного способа упорядочить дочерние коллекции в EF. Кто-нибудь может помочь?
4 ответа
Вы можете загрузить данные и отсортировать их в памяти после загрузки.
IEnumerable<Team> teams = _ctx.Teams
.Include(x => x.TeamMembers)
.Include(x => x.TeamMembers.Select(u => u.User))
.Where(x => x.UserId == ownerUserId)
.OrderBy(x => x.Name).ToList();
foreach (var team in teams)
{
team.TeamMembers = team.TeamMembers.OrderBy(m => m.Name);
foreach (var teamMember in team.TeamMembers)
{
teamMember.Users = teamMember.Users.OrderBy(u => u.Name);
}
}
Или вы можете использовать проекции и использовать отслеживание изменений EF для сортировки вашей коллекции. Вот пример фильтрации Включить, но то же самое работает для Порядка.
К сожалению рвется (Include
) не поддерживает фильтрацию или сортировку загруженных дочерних коллекций.
Но, как указано в принятом ответе, вы можете отсортировать каждую коллекцию после того, как она была загружена. Я написал метод расширения, который легко сортирует коллекцию по дочернему свойству, поэтому переназначение не требуется:
public static class Extensions
{
public static void SortOn<T>(this List<T> enumerable, Expression<Func<T, object>> expression)
{
var type = expression.Parameters[0].Type;
var memberName = ((MemberExpression)((UnaryExpression)expression.Body).Operand).Member.Name;
enumerable.Sort((x, y) =>
{
var first = (IComparable)type.GetProperty(memberName).GetValue(x);
var second = (IComparable)type.GetProperty(memberName).GetValue(y);
return first.CompareTo(second);
});
}
}
Затем сортировка дочерних коллекций может быть кратко записана как:
foreach (var team in teams)
team.TeamMembers.SortOn(_ => _.Name);
Вы можете свалить свои команды и членов их команд в анонимный тип (вероятно, не то, что вы хотите), например так:
public IEnumerable<Team> GetAllTeamsWithMembers(int ownerUserId)
{
return (from t in _ctx.Teams
where t.UserId == ownerUserId
select new {
Team = t,
TeamMembers = t.TeamMembers.OrderBy(m => m.Name)
}).ToList()
}
Затем вы можете просмотреть их:
foreach(Team team in GetAllTeamsWithMembers(1234))
{
string teamName = team.Team.Name;
string firstTeamMemberName = team.TeamMembers.First().Name;
}
Обновление: для записи, я считаю, не использовать это решение, а сортировать каждую коллекцию в цикле или во время рендеринга / связывания.
Я удалил 2-е решение, поскольку было указано, что EF не может выделяться в сущности.
Мое решение:
var data = await db.lessonTasks
.Select(res => new
{
details = res,
appos = res.appos.OrderBy(ap=>ap.dateTime).ToList(),
})
.OrderBy(r=>r.appos.First().dateTime)
.ToListAsync();