web-dev-qa-db-fra.com

Ordre LINQ par colonne NULL où ordre est croissant et les caractères NULL en dernier

J'essaie de trier une liste de produits en fonction de leur prix.

L'ensemble de résultats doit répertorier les produits par prix, de bas en haut, dans la colonne LowestPrice. Cependant, cette colonne est nullable.

Je peux trier la liste par ordre décroissant comme suit:

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

Cependant, je n'arrive pas à comprendre comment trier cela par ordre croissant.

// i'd like: 100, 101, 102, null, null
116
sf.

Essayez de mettre les deux colonnes dans le même ordre.

orderby p.LowestPrice.HasValue descending, p.LowestPrice

Sinon, chaque commande est une opération distincte sur la collection, en la réorganisant à chaque fois.

Ceci devrait ordonner ceux avec une valeur en premier, "ensuite" l'ordre de la valeur.

144
DaveShaw

Il est vraiment utile de comprendre la syntaxe de la requête LINQ et comment elle est traduite en appels de méthode LINQ.

Il se trouve que

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

sera traduit par le compilateur en

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

Ce n'est pas ce que vous voulez. Cela trie par Product.LowestPrice.HasValue dans descending dans l’ordre, puis trie de nouveau la collection entière par Product.LowestPrice dans descending ordre.

Ce que tu veux c'est

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

que vous pouvez obtenir en utilisant la syntaxe de requête par

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

Pour plus de détails sur les traductions de la syntaxe de requête aux appels de méthode, voir la spécification de langue. Sérieusement. Lis le.

79
jason

La solution pour les valeurs de chaîne est vraiment bizarre:

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

La seule raison qui fonctionne est que la première expression, OrderBy(), sort boolvaleurs: true/false. false result va d'abord suivi par le true result (nullables) et ThenBy() trier les valeurs non nulles par ordre alphabétique.

Donc, je préfère faire quelque chose de plus lisible, comme ceci:

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

Si SomeString est nul, il sera remplacé par "z", Puis tout sera trié par ordre alphabétique.

NOTE: Ce n'est pas une solution ultime car "z" Est le premier à passer des valeurs z telles que zebra.

UPDATE 06/09/2016 - À propos du commentaire @jornhd, c'est vraiment une bonne solution, mais c'est quand même un peu complexe, je recommanderai donc de l'envelopper dans une classe d'extension, comme ceci:

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

Et simple utilisez-le comme:

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

J'ai une autre option dans cette situation. Ma liste est objList, et je dois passer commande mais les null doivent être à la fin. ma décision:

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

J'essayais de trouver une solution LINQ à cela, mais je ne pouvais pas comprendre les réponses ici.

Ma réponse finale était:

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

ma décision:

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

C’est ce que j’ai trouvé parce que j’utilise des méthodes d’extension et que mon élément est une chaîne, donc pas de .HasValue:

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

Cela fonctionne avec les objets LINQ 2 en mémoire. Je ne l'ai pas testé avec EF ou avec aucun autre ORM de DB.

5
AaronLS

Vous trouverez ci-dessous une méthode d'extension permettant de rechercher la valeur null si vous souhaitez trier la propriété enfant d'un keySelector.

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

Et simple utilisez-le comme:

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