Как заказать дочерние коллекции сущностей в 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();
Другие вопросы по тегам