Pedido LINQ por columna nula donde el orden es ascendente y los valores nulos deben ser los últimos

Estoy tratando de ordenar una lista de productos por su precio.

El conjunto de resultados debe LowestPrice productos por precio de LowestPrice a mayor en la columna LowestPrice . Sin embargo, esta columna es nulable.

Puedo ordenar la lista en orden descendente así:

 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 

Sin embargo, no puedo entender cómo ordenar esto en orden ascendente.

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

Intenta poner ambas columnas en el mismo orden por.

 orderby p.LowestPrice.HasValue descending, p.LowestPrice 

De lo contrario, cada orden de pedido es una operación separada en la colección que lo reordena cada vez.

Esto debería ordenar primero los que tienen a con un valor, “luego” el orden del valor.

Realmente ayuda entender la syntax de la consulta LINQ y cómo se traduce a las llamadas al método LINQ.

Resulta que

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

será traducido por el comstackdor a

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

Esto enfáticamente no es lo que quieres. Esto se ordena según Product.LowestPrice.HasValue en orden descending y luego vuelve a ordenar toda la colección por Product.LowestPrice en orden descending .

Lo que quieres es

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

que puedes obtener usando la syntax de la consulta por

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

Para obtener detalles de las traducciones de syntax de consulta a llamadas de método, consulte la especificación de idioma. Seriamente. Léelo.

Tengo otra opción en esta situación. Mi lista es objList, y tengo que ordenar, pero los nulos deben estar al final. mi decisión:

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

La solución para los valores de cadena es realmente extraña:

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

La única razón por la que funciona es porque la primera expresión, OrderBy() , ordena los valores de bool : true / false . resultado false vaya primero a seguir por el resultado true ( ThenBy() ) y ThenBy() ordenar alfabéticamente los valores no nulos.

Entonces, prefiero hacer algo más legible como este:

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

Si SomeString es nulo, se reemplazará por "z" y luego ordenará todo alfabéticamente.

NOTA: Esta no es una solución definitiva ya que "z" va primero que los valores z como zebra .

ACTUALIZACIÓN 06/09/2016 – Sobre el comentario de @jornhd, es realmente una buena solución, pero sigue siendo un poco compleja, por lo que recomendaré envolverlo en una clase de extensión, como esta:

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

Y simple usarlo como:

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

mi decisión:

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

Esto es lo que se me ocurrió porque estoy usando métodos de extensión y también mi artículo es una cadena, por lo tanto, no .HasValue :

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

Esto funciona con los objetos LINQ 2 en la memoria. No lo probé con EF ni con ningún DB ORM.

Estaba tratando de encontrar una solución LINQ para esto, pero no pude resolverlo a partir de las respuestas aquí.

Mi respuesta final fue:

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

A continuación se muestra el método de extensión para buscar nulo si desea ordenar en la propiedad secundaria de un keySelector.

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

Y simple usarlo como:

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