"Ordina per" multiplo in LINQ


1582

Ho due tabelle moviese categoriesottengo prima un elenco ordinato per ID categoria e poi per nome .

La tabella dei filmati ha tre colonne ID, Nome e IDCategoria . La tabella delle categorie ha due colonne ID e Nome .

Ho provato qualcosa di simile al seguente, ma non ha funzionato.

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

Ecco perché questo non può funzionare: L'espressione lambda tra parentesi dovrebbe restituire un valore che può essere utilizzato per ordinare gli articoli: m.CategoryID è un numero che può essere utilizzato per ordinare gli articoli. Ma "m.CategoryID, m.Name" non ha senso in questo contesto.
Chiccodoro,

9
Allora, cosa stai cercando?
eka808,

Se per caso vuoi ordinarli in ordine decrescente, ecco la strada da percorrere.
RBT

Risposte:


2831

Questo dovrebbe funzionare per te:

var movies = _db.Movies.OrderBy(c => c.Category).ThenBy(n => n.Name)

4
Grazie per la risposta ovviamente ... Ma invece di Var movies = _db.Movies.Orderby(c => c.Category).ThenBy(n => n.Name) usare Var movies = _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name) 2 volte "orderBy" perché il risultato è diverso?

147
@devendra, il risultato è diverso perché il secondo "OrderBy" funziona sulla raccolta che è il risultato del primo "OrderBy" e riordina i suoi articoli

68
Come diavolo sono passato tutto questo tempo senza saperlo ThenBy?! ( Modifica: sembra che sia stato introdotto in .NET 4.0, il che spiega come mi è passato inosservato.)
Jordan Gray,

13
Questo è stato lì da quando è stato aggiunto LINQ. Questa risposta è precedente a .NET 4.0.
Nathan W,

10
Sì, ho concluso che troppo rapidamente basato sulla 3.5 non si trova nel menu a discesa della versione nella pagina della documentazione ; Avrei dovuto cercare fino in fondo le informazioni sulla versione. Grazie per la correzione. :)
Jordan Gray,

594

Utilizzando LINQ non-lambda, di sintassi delle query, è possibile effettuare ciò:

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

[MODIFICA per rispondere al commento] Per controllare l'ordinamento, utilizzare le parole chiave ascending(che è l'impostazione predefinita e quindi non particolarmente utile) o descending, in questo modo:

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

1
Non c'è modo di alternare tra discendente e non in questa sintassi c'è?
ehdv,

1
In realtà, la tua risposta è l'equivalente di _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). Più corretto èfrom row in _db.Movies orderby row.Category descending orderby row.Name select row
Lodewijk

9
@Lodewijk: credo che tu l'abbia esattamente al contrario. Il tuo esempio finirà per avere row.Name essendo la colonna e la riga primaria.Categoria secondaria, che è l'equivalente di _db.Movies.Orderby(c => c.Category).OrderBy(n => n.Name). I due frammenti forniti sono equivalenti tra loro, non ai PO.
Scott Stafford,

6
L'unico aspetto negativo dell'utilizzo della sintassi SQL per Linq è che non tutte le funzioni sono supportate, la maggior parte ma non tutte
Joshua G

74

Aggiungere nuova":

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

Funziona sulla mia scatola. Restituisce qualcosa che può essere usato per ordinare. Restituisce un oggetto con due valori.

Simile, ma diverso dall'ordinamento per colonna combinata, come segue.

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

21
Fai attenzione quando lo usi per i numeri.
WoF_Angel,

7
È possibile utilizzare OrderByDescending e ThenBy o OrderBy e ThenByDescending, a seconda delle esigenze.
Ali Shah Ahmed,

6
Sono abbastanza sicuro che .OrderBy( m => new { m.CategoryID, m.Name })e .OrderBy( m => new { m.Name, m.CategoryID })produrrà gli stessi risultati, piuttosto che rispettando la priorità di destinazione. A volte sembrerà darti l'ordine che desideri puramente per coincidenza. Inoltre m.CategoryID.ToString() + m.Nameprodurrà ordini errati se CategoryID è un int. Ad esempio, qualcosa con id = 123, name = 5times apparirà dopo id = 1234, name = qualcosa anziché prima. Inoltre, non è inefficiente eseguire confronti di stringhe in cui potrebbero verificarsi confronti di int.
AaronS

7
Quando provo a ordinare in base a un tipo anonimo, ottengo un ArgumentException con il messaggio "Almeno un oggetto deve implementare IComparable.". Vedo che altri devono dichiarare un comparatore quando lo fanno. Vedi stackoverflow.com/questions/10356864/… .
Robert Gowland,

4
Questo è assolutamente sbagliato. L'ordinamento con un nuovo tipo anonimo che non ha un'implementazione ICompariable non può funzionare, perché non esiste un ordine per le proprietà di un tipo anonimo. Non saprebbe se ordinare prima su IDCategoria o Nome, figuriamoci se fossero ordinati in ordini opposti.
Triynko,

29

usa la seguente riga su DataContext per registrare l'attività SQL su DataContext sulla console - quindi puoi vedere esattamente ciò che le tue istruzioni linq richiedono dal database:

_db.Log = Console.Out

Le seguenti istruzioni LINQ:

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

E

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

produce il seguente SQL:

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

Considerando che, ripetendo un OrderBy in Linq, sembra invertire l'output SQL risultante:

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

E

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

produce il seguente SQL (Nome e CategoryId sono cambiati):

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

24

Ho creato alcuni metodi di estensione (sotto), quindi non devi preoccuparti se un IQueryable è già stato ordinato o meno. Se vuoi ordinare per più proprietà, fallo come segue:

// 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);

Ciò è particolarmente utile se si crea l'ordinamento in modo dinamico, ad esempio da un elenco di proprietà da ordinare.

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);
        }
    }
}

1
Questa risposta è oro! Io combinare il controllo per queryable.IsOrdered () con la risposta da questo post, di avere un unico metodo che prende una direzione sorta: stackoverflow.com/questions/388708
SwissCoder

1
In questo modo l'implementazione di Linq dovrebbe essere al primo posto! OrderBy è mal progettato ...
Andrzej Martyna

1
@Marie chiaramente non hai capito il caso d'uso. Come si crea un ordine in modo dinamico da un elenco di proprietà, ad esempio? Questa è l'unica strada. Si prega di non sottovalutare e riconsiderare. Grazie.
sjkm,

Giusto. Non vedo ancora il vantaggio, ma riprenderò il mio voto
Marie,

funzionerà connullable datetime
aggie il

16

Esiste almeno un altro modo per farlo utilizzando LINQ, sebbene non sia il più semplice. Puoi farlo usando il OrberBy()metodo che usa un IComparer. Per prima cosa devi implementare un IComparerper la Movieclasse in questo modo:

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);
        }
    }
}

Quindi è possibile ordinare i film con la seguente sintassi:

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

Se è necessario passare l'ordinamento in ordine decrescente per uno degli articoli, basta cambiare xey all'interno del Compare() metodo di MovieComparerconseguenza.


1
Mi piace che sia più generale di allora poiché puoi fare cose strane con il confronto incluso avere diversi oggetti di confronto con algoritmi diversi pronti per l'uso. Questo è meglio della mia soluzione preferita prima di apprendere a quel punto che era creare una classe che implementasse l'interfaccia IComparable.
Gerard ONeill,

1
Dal 2012 (.NET versione 4.5) non è necessario creare una classe da MovieComparersoli; invece puoi farlo _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); } }));. Naturalmente, se preferisci scrivere la logica come una sola espressione, anziché if... else, allora il lamda (x, y) => exprpuò essere più semplice.
Jeppe Stig Nielsen,

3

Se utilizza un repository generico

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

altro

> _db.Module.Where(x=> ......).OrderBy(x => new { x.Level, x.Rank}).ToList();

1
Le espressioni anonime verranno analizzate localmente dal core del framework di entità. L'espressione LINQ non può essere tradotta e verrà valutata localmente.
alhpe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.