aufsteigend / absteigend in LINQ - kann man die Reihenfolge über Parameter ändern?

77

Ich habe eine Methode, die den Parameter "bool sortAscending" erhält. Jetzt möchte ich LINQ verwenden, um abhängig von diesem Parameter eine sortierte Liste zu erstellen. Ich habe dann folgendes:

var ascendingQuery = from data in dataList
                      orderby data.Property ascending
                      select data;

var descendingQuery = from data in dataList
                      orderby data.Property descending
                      select data;

Wie Sie sehen können, unterscheiden sich beide Abfragen nur in "aufsteigend" bzw. "absteigend". Ich möchte beide Abfragen zusammenführen, weiß aber nicht wie. Hat jemand die Antwort?

Johannes
quelle
Sie möchten also eine Abfrage haben, die je nach bool sortAsvending-Wert sowohl aufsteigend als auch absteigend ausgeführt werden kann? Ist das korrekt.
Nathan W

Antworten:

119

Sie können ganz einfach Ihre eigene Erweiterungsmethode für IEnumerable oder IQueryable erstellen:

public static IOrderedEnumerable<TSource> OrderByWithDirection<TSource,TKey>
    (this IEnumerable<TSource> source,
     Func<TSource, TKey> keySelector,
     bool descending)
{
    return descending ? source.OrderByDescending(keySelector)
                      : source.OrderBy(keySelector);
}

public static IOrderedQueryable<TSource> OrderByWithDirection<TSource,TKey>
    (this IQueryable<TSource> source,
     Expression<Func<TSource, TKey>> keySelector,
     bool descending)
{
    return descending ? source.OrderByDescending(keySelector)
                      : source.OrderBy(keySelector);
}

Ja, Sie verlieren hier die Möglichkeit, einen Abfrageausdruck zu verwenden - aber ehrlich gesagt glaube ich nicht, dass Sie in diesem Fall tatsächlich von einem Abfrageausdruck profitieren. Abfrageausdrücke eignen sich hervorragend für komplexe Aufgaben. Wenn Sie jedoch nur eine einzige Operation ausführen, ist es einfacher, nur diese eine Operation auszuführen:

var query = dataList.OrderByWithDirection(x => x.Property, direction);
Jon Skeet
quelle
4
Danke, dass du meine Dummheit korrigiert hast Marc :) (Das ist das Problem beim Posten kurz vor dem Mittagessen ...)
Jon Skeet
9
Dieser Beitrag wurde von zwei Prominenten von SO.com beantwortet und bearbeitet
Johnny_D
Sollte eine der OrderByWithDirections nicht aufsteigend zurückkehren?
Ctrl_Alt_Defeat
1
@KOL: Ich bin nicht sicher, was du meinst. Beide kehren aufsteigend zurück, wenn Sie falsefür übergeben descending.
Jon Skeet
@ JonSkeet, danke für die Lösung, aber ich habe eine Frage zur Verwendung gestellt. siehe: stackoverflow.com/questions/18348767/…
ekkis
42

In Bezug auf die Implementierung ändert dies die Methode - von OrderBy / ThenBy zu OrderByDescending / ThenByDescending. Sie können die Sortierung jedoch separat auf die Hauptabfrage anwenden ...

var qry = from .... // or just dataList.AsEnumerable()/AsQueryable()

if(sortAscending) {
    qry = qry.OrderBy(x=>x.Property);
} else {
    qry = qry.OrderByDescending(x=>x.Property);
}

Irgendeine Verwendung? Sie können die gesamte "Bestellung" dynamisch erstellen, dies ist jedoch aufwändiger ...

Ein weiterer Trick (hauptsächlich für LINQ-to-Objects geeignet) ist die Verwendung eines Multiplikators von -1/1. Dies ist nur für numerische Daten wirklich nützlich, aber ein frecher Weg, um das gleiche Ergebnis zu erzielen.

Marc Gravell
quelle
1
Ich wollte gerade genau das Gleiche schreiben, als mein VS erstarrte :(
Nathan W
1
Die Verwendung eines Multiplikators schlägt auch für einen Rückgabewert von int.MinValue fehl.
Jon Skeet
Warum verwenden Sie die orderBy-Methode anstelle des orderby-Schlüsselworts in einer linq-Abfrage?
Joshit
@Joshit, da ich in beiden Fällen unterschiedliche Operationen ausführen muss und die Verwendung der LINQ-Abfragesyntax hier keinen Vorteil gegenüber der Verwendung der Erweiterungsmethode bietet. Wenn Sie die Abfragesyntax bevorzugen: Machen Sie das, es ist in Ordnung.
Marc Gravell
@MarcGravell vielen Dank für Ihre schnelle Antwort! Ich dachte, wenn zwei Größen der Performance-Programmierung es so machen, lohnt es sich zu fragen, ob es tiefere Gründe gibt;)
Joshit
8

Was ist mit der Bestellung nach der gewünschten Eigenschaft?

   blah = blah.OrderByDescending(x => x.Property);

Und dann so etwas tun

  if (!descending)
  {
       blah = blah.Reverse()
  }
  else
  {
      // Already sorted desc ;)
  }

Ist es Reverse () zu langsam?

Sport
quelle
Wahrscheinlich nicht der optimierteste, aber sicherlich ein sehr kluger Ansatz
Diego Penha
@DiegoPenha Humm, aber für den Fall, dass es sich um Entitäten handelt, bevor Daten materialisiert werden, sollten sie gleich sein, nicht wahr? Ich meine, wird eine SQL-Abfrage entsprechend erzeugen. Du stimmst zu?
gsubiran
@DiegoPenha vergiss es, linq to entity unterstützt nicht .Reverse () -Funktion 😪
gsubiran
3

Zusätzlich zu der schönen Lösung von @Jon Skeet brauchte ich auch ThenBy und ThenByDescending, also füge ich sie basierend auf seiner Lösung hinzu:

    public static IOrderedEnumerable<TSource> ThenByWithDirection<TSource, TKey>(
         this IOrderedEnumerable<TSource> source, 
         Func<TSource, TKey> keySelector,  
         bool descending)
    {
        return descending ? 
               source.ThenByDescending(keySelector) :
               source.ThenBy(keySelector);
    }
ehh
quelle