Несколько «упорядочить по» в LINQ

1582

У меня есть две таблицы, moviesи categories, и я получаю упорядоченный список сначала по categoryID, а затем по имени .

Таблица фильмов имеет три столбца ID, Имя и CategoryID . Таблица категорий имеет два столбца ID и Имя .

Я попробовал что-то вроде следующего, но это не сработало.

var movies = _db.Movies.OrderBy( m => { m.CategoryID, m.Name })
Саша
источник
Вот почему это не может работать: лямбда-выражение в скобках должно возвращать значение, которое можно использовать для упорядочивания элементов: m.CategoryID - это число, которое можно использовать для упорядочения элементов. Но «m.CategoryID, m.Name» не имеет смысла в этом контексте.
Чиккодоро
9
.То, что вы ищете?
eka808
Если по какой-либо причине вы хотите отсортировать их в порядке убывания, то здесь есть путь.
RBT

Ответы:

2831

Это должно работать для вас:

var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name)
Натан У
источник
4
Спасибо за ответ, конечно ... Но вместо того, Var movies = _db.Movies.Orderby(c => c.Category).ThenBy(n => n.Name) чтобы использовать Var movies = _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name) 2 раза "orderBy", почему результат отличается?
147
@devendra, результат другой, потому что второй «OrderBy» работает над коллекцией, которая является результатом первого «OrderBy», и переупорядочивает его элементы
68
Как же я прошел все это время, не зная о чем ThenBy?! ( Изменить: похоже, что он был введен в .NET 4.0, который объясняет, как он незаметно проскользнул мимо меня.)
Джордан Грей,
13
Это было там с тех пор, как был добавлен LINQ. Этот ответ до .NET 4.0.
Натан W
10
Да, я пришел к выводу, что слишком поспешно на основании 3.5 не находится в выпадающей версии на странице документации ; Я должен был искать информацию о версии. Спасибо за исправление. :)
Джордан Грей
594

Используя не лямбда, синтаксис запроса LINQ, вы можете сделать это:

var movies = from row in _db.Movies 
             orderby row.Category, row.Name
             select row;

[РЕДАКТИРОВАТЬ к адресу комментария] Чтобы управлять порядком сортировки, используйте ключевые слова ascending(которые используются по умолчанию и поэтому не особенно полезны) или descending, например, так:

var movies = from row in _db.Movies 
             orderby row.Category descending, row.Name
             select row;
Скотт Стаффорд
источник
1
Там нет способа переключаться между нисходящим и не в этом синтаксисе есть?
ehdv
1
На самом деле, ваш ответ эквивалентен _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). Более правильным являетсяfrom row in _db.Movies orderby row.Category descending orderby row.Name select row
Lodewijk
9
@Lodewijk: Я полагаю, что у вас есть все это задом наперед. Ваш пример в конечном итоге будет иметь row.Name, являющийся первичным столбцом, и row.Category вторичный, что эквивалентно _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). Два предоставленных вами фрагмента эквивалентны друг другу, а не ОП.
Скотт Стаффорд
6
Единственным недостатком использования синтаксиса SQL для Linq является то, что поддерживаются не все функции, большинство, но не все
Джошуа Дж
74

Добавить новое":

var movies = _db.Movies.OrderBy( m => new { m.CategoryID, m.Name })

Это работает на моей коробке. Он возвращает что-то, что можно использовать для сортировки. Возвращает объект с двумя значениями.

Аналогично, но отличается от сортировки по объединенному столбцу следующим образом.

var movies = _db.Movies.OrderBy( m => (m.CategoryID.ToString() + m.Name))
Alex
источник
21
Будьте осторожны при использовании этого для чисел.
WoF_Angel
7
Вы можете использовать OrderByDescending и ThenBy, или OrderBy и ThenByDescending, в зависимости от ваших потребностей.
Али Шах Ахмед
6
Я вполне уверен, что .OrderBy( m => new { m.CategoryID, m.Name })и .OrderBy( m => new { m.Name, m.CategoryID })получу те же результаты, чем при уважении предполагаемого приоритета. Иногда может показаться, что вы заказываете, что вам нужно, просто по стечению обстоятельств. Кроме того, m.CategoryID.ToString() + m.Nameбудут произведены неправильные заказы, если CategoryID является int. Например, что-то с id = 123, name = 5times появится после id = 1234, name = что-то вместо прежнего. Также неэффективно проводить сравнения строк, где могут происходить сравнения int.
AaronLS
7
Когда я пытаюсь упорядочить по анонимному типу, я получаю ArgumentException с сообщением «По крайней мере, один объект должен реализовывать IComparable». Я вижу, что другим приходится объявлять компаратор при этом. См. Stackoverflow.com/questions/10356864/… .
Роберт Гоуленд,
4
Это абсолютно неправильно. Упорядочение по новому анонимному типу, который не имеет реализации ICompariable, не может работать, потому что нет порядка в свойствах анонимного типа. Он не будет знать, нужно ли сначала сортировать по CategoryID или по Name, не говоря уже о том, должны ли они быть отсортированы в противоположных порядках.
Триынко
29

используйте следующую строку в вашем DataContext, чтобы записать действие SQL в DataContext на консоль - тогда вы сможете точно увидеть, что ваши операторы linq запрашивают из базы данных:

_db.Log = Console.Out

Следующие операторы LINQ:

var movies = from row in _db.Movies 
             orderby row.CategoryID, row.Name
             select row;

А ТАКЖЕ

var movies = _db.Movies.OrderBy(m => m.CategoryID).ThenBy(m => m.Name);

создайте следующий SQL:

SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].CategoryID, [t0].[Name]

Принимая во внимание, что повторение OrderBy в Linq, по-видимому, обращает результирующий вывод SQL:

var movies = from row in _db.Movies 
             orderby row.CategoryID
             orderby row.Name
             select row;

А ТАКЖЕ

var movies = _db.Movies.OrderBy(m => m.CategoryID).OrderBy(m => m.Name);

создайте следующий SQL (Name и CategoryId переключаются):

SELECT [t0].ID, [t0].[Name], [t0].CategoryID
FROM [dbo].[Movies] as [t0]
ORDER BY [t0].[Name], [t0].CategoryID
Оливер Слэй
источник
24

Я создал несколько методов расширения (ниже), поэтому вам не нужно беспокоиться, заказан ли IQueryable или нет. Если вы хотите упорядочить по нескольким свойствам, просто сделайте это следующим образом:

// We do not have to care if the queryable is already sorted or not. 
// The order of the Smart* calls defines the order priority
queryable.SmartOrderBy(i => i.Property1).SmartOrderByDescending(i => i.Property2);

Это особенно полезно, если вы создаете порядок динамически, например, из списка свойств для сортировки.

public static class IQueryableExtension
{
    public static bool IsOrdered<T>(this IQueryable<T> queryable) {
        if(queryable == null) {
            throw new ArgumentNullException("queryable");
        }

        return queryable.Expression.Type == typeof(IOrderedQueryable<T>);
    }

    public static IQueryable<T> SmartOrderBy<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
        if(queryable.IsOrdered()) {
            var orderedQuery = queryable as IOrderedQueryable<T>;
            return orderedQuery.ThenBy(keySelector);
        } else {
            return queryable.OrderBy(keySelector);
        }
    }

    public static IQueryable<T> SmartOrderByDescending<T, TKey>(this IQueryable<T> queryable, Expression<Func<T, TKey>> keySelector) {
        if(queryable.IsOrdered()) {
            var orderedQuery = queryable as IOrderedQueryable<T>;
            return orderedQuery.ThenByDescending(keySelector);
        } else {
            return queryable.OrderByDescending(keySelector);
        }
    }
}
sjkm
источник
1
Этот ответ - золото! Я объединю проверку для queryable.IsOrdered () с ответом из этого поста, чтобы иметь единственный метод, который принимает направление сортировки: stackoverflow.com/questions/388708
SwissCoder
1
Таким образом, реализация Linq должна идти в первую очередь! OrderBy плохо спроектирован ...
Анджей Мартина
1
@ Мари, ты явно не понял вариант использования. Как вы, например, динамически создаете порядок из списка свойств? Это единственный способ. Пожалуйста, не отрицайте и не пересматривайте. Спасибо.
Sjkm
Справедливо. Я все еще не вижу выгоды, но я заберу свой голос назад
Мари
Будет ли это работать сnullable datetime
Aggie
16

Есть по крайней мере еще один способ сделать это с помощью LINQ, хотя и не самый простой. Вы можете сделать это с помощью OrberBy()метода, который использует IComparer. Сначала вам нужно реализовать IComparerдля Movieкласса:

public class MovieComparer : IComparer<Movie>
{
    public int Compare(Movie x, Movie y)
    {
        if (x.CategoryId == y.CategoryId)
        {
            return x.Name.CompareTo(y.Name);
        }
        else
        {
            return x.CategoryId.CompareTo(y.CategoryId);
        }
    }
}

Тогда вы можете заказать фильмы со следующим синтаксисом:

var movies = _db.Movies.OrderBy(item => item, new MovieComparer());

Если вам нужно переключить порядок по убыванию для одного из элементов, просто переключите x и y внутри Compare() метода MovieComparerсоответственно.

prudentcoder
источник
1
Мне нравится это как более общий, чем тогда, поскольку вы можете делать странные вещи со сравнением, в том числе иметь разные объекты сравнения с разными алгоритмами, готовыми к работе. Это лучше, чем мое предпочтительное решение, прежде чем узнавать о том, каким образом было создать класс, реализующий интерфейс IComparable.
Джерард ONeill
1
С 2012 года (.NET версия 4.5) вам не нужно создавать класс MovieComparerсамостоятельно; вместо этого вы можете сделать _db.Movies.OrderBy(item => item, Comparer<Movie>.Create((x, y) => { if (x.CategoryId == y.CategoryId) { return x.Name.CompareTo(y.Name); } else { return x.CategoryId.CompareTo(y.CategoryId); } }));. Конечно, если вы предпочитаете писать логику в виде одного выражения вместо if... else, тогда лямда (x, y) => exprможет быть проще.
Джеппе Стиг Нильсен
3

Если использовать общий репозиторий

> lstModule = _ModuleRepository.GetAll().OrderBy(x => new { x.Level,
> x.Rank}).ToList();

еще

> _db.Module.Where(x=> ......).OrderBy(x => new { x.Level, x.Rank}).ToList();
Сен-Мартен Гийом
источник
1
Анонимные выражения будут анализироваться локально ядром платформы сущностей. Выражение LINQ не может быть переведено и будет оцениваться локально.
Alhpe