LINQ order by null column其中order是升序,null应该是last

时间:2011-06-23 22:42:28

标签: c# linq sorting

我正在尝试按价格对产品列表进行排序。

结果集需要按列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

10 个答案:

答案 0 :(得分:139)

尝试将两列放在相同的顺序中。

orderby p.LowestPrice.HasValue descending, p.LowestPrice

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

这应该先命名带有值的那个,然后是“那个”值的顺序。

答案 1 :(得分:75)

理解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顺序排序,然后按Product.LowestPrice顺序按descending重新排序整个集合。

你想要的是

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;

有关从查询语法到方法调用的转换的详细信息,请参阅语言规范。认真。阅读它。

答案 2 :(得分:14)

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

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

答案 3 :(得分:13)

字符串值的解决方案非常奇怪:

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

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

所以,我更喜欢做一些比较可读的东西:

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

如果SomeString为空,则它将被"z"替换,然后按字母顺序对所有内容进行排序。

注意:这不是最终解决方案,因为"z"优先于zebra等z值。

更新 2016年9月6日 - 关于@jornhd评论,它确实是一个很好的解决方案,但它仍然有点复杂,所以我建议将它包装在Extension类中,例如这样:

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

答案 4 :(得分:7)

我的决定:

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

答案 5 :(得分:7)

我试图找到一个LINQ解决方案,但无法从这里的答案中解决。

我的最终答案是:

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

答案 6 :(得分:5)

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

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

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

答案 7 :(得分:0)

下面是用于对null进行扩展的方法,如果您想对keySelector的child属性进行排序。

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

使用简单,就像这样:

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

答案 8 :(得分:0)

这是另一种方式:

//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.

请注意,在Oracle数据库中将r.SUP_APPROVED_IND.Trim() == null视为trim(SUP_APPROVED_IND) is null

有关详情,请参见:How can i query for null values in entity framework?

答案 9 :(得分:0)

另一个选择(在我们的情况下很方便):

我们有一个用户表,存储着ADName, LastName, FirstName

  • 用户应按字母顺序
  • 也没有名字/姓氏的帐户,基于其ADName-但在用户列表的末尾
  • ID为“ 0”(“无选择”)的虚拟用户应始终位于最高位置。

我们更改了表架构,并添加了“ SortIndex”列,该列定义了一些排序组。 (我们的间隔为5,所以我们以后可以插入组)

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

现在,从查询角度来看,它将是:

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

在方法表达式中:

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

产生预期的结果:

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