LINQ-Reihenfolge nach Nullspalte, wobei die Reihenfolge aufsteigend ist und Nullen zuletzt sein sollten


141

Ich versuche, eine Liste der Produkte nach ihrem Preis zu sortieren.

Die Ergebnismenge muss Produkte nach Preis von niedrig nach hoch in der Spalte auflisten LowestPrice . Diese Spalte ist jedoch nullwertfähig.

Ich kann die Liste in absteigender Reihenfolge wie folgt sortieren:

var products = from p in _context.Products
   where p.ProductTypeId == 1
   orderby p.LowestPrice.HasValue descending
   orderby p.LowestPrice descending
   select p;

// returns:    102, 101, 100, null, null

Ich kann jedoch nicht herausfinden, wie ich das in aufsteigender Reihenfolge sortieren soll.

// i'd like: 100, 101, 102, null, null

11
orderby p.LowestPrice ?? Int.MaxValue;ist ein einfacher Weg.
PostMan

3
@PostMan: Ja, es ist einfach, es erzielt das richtige Ergebnis, ist aber OrderByDescending, ThenByklarer.
Jason

@ Jason, ja, ich kannte die Syntax für das nicht orderbyund wurde von der Seite verfolgt, um danach zu suchen :)
PostMan

Antworten:


160

Versuchen Sie, beide Spalten in die gleiche Reihenfolge zu bringen.

orderby p.LowestPrice.HasValue descending, p.LowestPrice

Andernfalls ist jede Bestellung ein separater Vorgang für die Sammlung, die jedes Mal neu bestellt wird.

Dies sollte zuerst diejenigen mit einem Wert ordnen, dann "dann" die Reihenfolge des Wertes.


21
Häufiger Fehler: Mit der Lamda-Syntax machen die Leute dasselbe. Sie verwenden .OrderBy zweimal anstelle von .ThenBy.
DaveShaw

1
Dies funktionierte , um Felder mit Werten oben und Nullfeldern unten zu ordnen. Ich habe Folgendes verwendet: orderby p.LowestPrice == null, p.LowestPrice ascending Hoffnung hilft jemandem.
Shaijut

@ DaveShaw danke für den Tipp - vor allem den Kommentar - sehr ordentlich - liebe es
Demetris Leptos

86

Es ist sehr hilfreich, die LINQ-Abfragesyntax zu verstehen und zu verstehen, wie sie in LINQ-Methodenaufrufe übersetzt wird.

Es stellt sich heraus, dass

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending
               orderby p.LowestPrice descending
               select p;

wird vom Compiler nach übersetzt

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .OrderByDescending(p => p.LowestPrice)
                       .Select(p => p);

Dies ist nachdrücklich nicht das, was Sie wollen. Diese Art von Product.LowestPrice.HasValuein descendingOrdnung und dann neu sortiert die gesamte Kollektion von Product.LowestPricein descendingOrdnung.

Was du willst ist

var products = _context.Products
                       .Where(p => p.ProductTypeId == 1)
                       .OrderByDescending(p => p.LowestPrice.HasValue)
                       .ThenBy(p => p.LowestPrice)
                       .Select(p => p);

die Sie mit der Abfragesyntax von erhalten können

var products = from p in _context.Products
               where p.ProductTypeId == 1
               orderby p.LowestPrice.HasValue descending,
                       p.LowestPrice
               select p;

Einzelheiten zu den Übersetzungen von der Abfragesyntax zu Methodenaufrufen finden Sie in der Sprachspezifikation. Ernsthaft. Lies es.


4
+1 oder einfach ... schreibe nicht die LINQ-Abfragesyntax :) Trotzdem gute Erklärung
sehe

18

Die Lösung für Zeichenfolgenwerte ist wirklich komisch:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString) 

Der einzige Grund, der funktioniert, ist, dass der erste Ausdruck Werte OrderBy()sortiert bool: true/ false. falseDas Ergebnis folgt zuerst dem trueErgebnis (nullables) und ThenBy()sortiert die Nicht-Null-Werte alphabetisch.

Also mache ich lieber etwas Lesbareres wie dieses:

.OrderBy(f => f.SomeString ?? "z")

Wenn SomeStringnull ist, wird es durch ersetzt "z"und sortiert dann alles alphabetisch.

HINWEIS: Dies ist keine ultimative Lösung, da "z"zuerst Z-Werte wie z zebra.

UPDATE 9/6/2016 - Über @ jornhd Kommentar, es ist wirklich eine gute Lösung, aber es ist immer noch ein wenig komplex, daher werde ich empfehlen, es in eine Erweiterungsklasse wie diese zu packen:

public static class MyExtensions
{
    public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, string> keySelector)
    {
        return list.OrderBy(v => keySelector(v) != null ? 0 : 1).ThenBy(keySelector);
    }
}

Und verwenden Sie es einfach wie:

var sortedList = list.NullableOrderBy(f => f.SomeString);

2
Ich denke, dies ist besser lesbar, ohne die böse Konstante: .OrderBy (f => f.SomeString! = Null? 0: 1) .ThenBy (f => f.SomeString)
jornhd

14

Ich habe in dieser Situation eine andere Option. Meine Liste ist objList und ich muss bestellen, aber am Ende müssen Nullen stehen. meine Entscheidung:

var newList = objList.Where(m=>m.Column != null)
                     .OrderBy(m => m.Column)
                     .Concat(objList.where(m=>m.Column == null));

Dies kann in Szenarien funktionieren, in denen ein Ergebnis mit anderen Werten wie 0 anstelle von Null erzielt werden soll.
Naresh Ravlani

Ja. Ersetzen Sie einfach null durch 0.
Gurgen Hovsepyan

Dies ist die einzige Antwort, die für mich funktioniert hat, der Rest hat die Nullen am Anfang der Liste beibehalten.
BMills

9

Ich habe versucht, eine LINQ-Lösung für dieses Problem zu finden, konnte es aber aus den Antworten hier nicht herausfinden.

Meine endgültige Antwort war:

.OrderByDescending(p => p.LowestPrice.HasValue).ThenBy(p => p.LowestPrice)

7

meine Entscheidung:

Array = _context.Products.OrderByDescending(p => p.Val ?? float.MinValue)

7

Dies ist, was ich mir ausgedacht habe, weil ich Erweiterungsmethoden verwende und mein Element auch eine Zeichenfolge ist, also nein .HasValue:

.OrderBy(f => f.SomeString == null).ThenBy(f => f.SomeString)

Dies funktioniert mit LINQ 2-Objekten im Speicher. Ich habe es nicht mit EF oder einem DB ORM getestet.


0

Im Folgenden finden Sie eine Erweiterungsmethode, mit der Sie nach Null suchen können, wenn Sie nach der untergeordneten Eigenschaft eines keySelector sortieren möchten.

public static IOrderedEnumerable<T> NullableOrderBy<T>(this IEnumerable<T> list, Func<T, object> parentKeySelector, Func<T, object> childKeySelector)
{
    return list.OrderBy(v => parentKeySelector(v) != null ? 0 : 1).ThenBy(childKeySelector);
}

Und verwenden Sie es einfach wie:

var sortedList = list.NullableOrderBy(x => x.someObject, y => y.someObject?.someProperty);

0

Hier ist ein anderer Weg:

//Acsending
case "SUP_APPROVED_IND": qry =
                            qry.OrderBy(r => r.SUP_APPROVED_IND.Trim() == null).
                                    ThenBy(r => r.SUP_APPROVED_IND);

                            break;
//….
//Descending
case "SUP_APPROVED_IND": qry =
                            qry.OrderBy(r => r.SUP_APPROVED_IND.Trim() == null).
                                    ThenByDescending(r => r.SUP_APPROVED_IND); 

                            break;

SUP_APPROVED_IND is char(1) in Oracle db.

Beachten Sie, dass r.SUP_APPROVED_IND.Trim() == nullbehandelt wird alstrim(SUP_APPROVED_IND) is null in Oracle db behandelt wird.

Weitere Informationen finden Sie hier: Wie kann ich im Entity Framework nach Nullwerten fragen?


0

Eine weitere Option (war praktisch für unser Szenario):

Wir haben eine Benutzertabelle, die speichert ADName, LastName, FirstName

  • Benutzer sollten alphabetisch sein
  • Konten ohne Vor- / Nachnamen, ebenfalls basierend auf ihrem ADNamen - aber am Ende der Benutzerliste
  • Dummy-Benutzer mit der ID "0" ("Keine Auswahl") Sollte immer oben sein.

Wir haben das Tabellenschema geändert und eine "SortIndex" -Spalte hinzugefügt, die einige Sortiergruppen definiert. (Wir haben eine Lücke von 5 gelassen, damit wir später Gruppen einfügen können.)

ID | ADName |      First Name | LastName | SortIndex
0    No Selection  null         null     | 0
1    AD\jon        Jon          Doe      | 5
3    AD\Support    null         null     | 10     
4    AD\Accounting null         null     | 10
5    AD\ama        Amanda       Whatever | 5

Nun, in Bezug auf die Abfrage wäre es:

SELECT * FROM User order by SortIndex, LastName, FirstName, AdName;

in Methodenausdrücken:

db.User.OrderBy(u => u.SortIndex).ThenBy(u => u.LastName).ThenBy(u => u.FirstName).ThenBy(u => u.AdName).ToList();

was das erwartete Ergebnis ergibt:

ID | ADName |      First Name | LastName | SortIndex
0    No Selection  null         null     | 0
5    AD\ama        Amanda       Whatever | 5
1    AD\jon        Jon          Doe      | 5
4    AD\Accounting null         null     | 10
3    AD\Support    null         null     | 10     
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.