2011-06-23 59 views
83

我试图根据他们的价格对产品列表进行排序。LINQ order by null column where order is up and nulls should last

结果集需要按列LowestPrice列按价格从低到高列出产品。但是,此列可以为空。

我可以像这样降序排序列表:

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 

但我无法弄清楚如何进行升序排列此。

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

'的OrderBy p.LowestPrice? Int.MaxValue;'是一种简单的方法。 – PostMan

+2

@PostMan:是的,它很简单,它达到了正确的结果,但是'OrderByDescending,ThenBy'更清晰。 – jason

+0

@Jason,是的,我不知道'orderby'的语法,并得到了侧面追踪寻找:) – PostMan

回答

114

试着把两列放在同一个orderby中。

orderby p.LowestPrice.HasValue descending, p.LowestPrice 

否则每个orderby是集合上的单独操作,每次重新排序它。

这应该先将a和a的值先排序,然后再排序。

+3

欢呼声。这么简单,我觉得自己像一个白痴:P –

+11

常见的错误,人们用Lamda Syntax做同样的事情 - 使用.OrderBy而不是.ThenBy。 – DaveShaw

+0

**这个工作**订购,顶部和空字段在底部我用这个值的字段: '排序依据p.LowestPrice == NULL,p.LowestPrice ascending' 希望可以帮助别人。 – stom

59

它真的有助于理解LINQ查询语法以及它如何转换为LINQ方法调用。

事实证明,

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

将被编译器转换为

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

这显然不是你想要的。这种排序Product.LowestPrice.HasValuedescending订单,然后通过descending订购Product.LowestPrice重新整理整个集合。

你想要的是

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

您可以通过

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

获得使用查询语法翻译的细节从查询语法的方法调用,请参阅语言规范。认真。阅读。

+3

+1或者只是...不写LINQ查询语法:)很好的解释然而 – sehe

11

我在这种情况下有另一种选择。 我的列表是objList,我必须排序,但空值必须在最后。 我的决定:

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

这可以在需要结果的场景中工作与其他值0代替null。 –

+0

是的。只需将null替换为0. –

8

我的决定:

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

这是我想出了,因为我使用的扩展方法,也是我的项目是一个字符串,因此没有.HasValue

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

这适用于内存中的LINQ 2对象。我没有用EF或任何DB ORM进行测试。

4

我正试图找到一个LINQ解决方案,但无法解决这个问题。

我最后的答案是:

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

字符串值解决方案是很奇怪:

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

的作品的唯一原因是因为第一个表达式,OrderBy(),排序bool值:true/falsefalse结果首先按true结果(可空)和ThenBy()按字母顺序对非空值进行排序。

所以,我更喜欢做一些更具可读性像这样:

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

如果SomeString为空,它将被"z"被替换,然后按字母顺序排序的一切。

注意:这不是一个最终的解决方案,因为"z"首先比z值更好,比如zebra

UPDATE 2016年9月6日 - 关于@jornhd评论,它确实是一个很好的解决方案,但它仍然是一个有点复杂,所以我会推荐给它包装在一个扩展类,比如这个:

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

而单纯使用它像:

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

我认为这更可读,没有讨厌的常量: .OrderBy(f => f.SomeString!= null?0:1)。ThenBy(f => f.SomeString) – jornhd