LINQorder by null 列,其中 order 为升序,null 应为最后一个

我正试图按价格对产品列表进行分类。

结果集需要通过 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
126742 次浏览

尝试将两列放在同一个 orderby 中。

orderby p.LowestPrice.HasValue descending, p.LowestPrice

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

这应该先排序有值的那些,“然后”是值的顺序。

它确实有助于理解 LINQ 查询语法以及如何将其转换为 LINQ 方法调用。

原来

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

将由编译器翻译为

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

这绝对不是你想要的。按 descending顺序按 Product.LowestPrice.HasValue排序,然后按 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;

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

在这种情况下我还有别的选择。 我的列表是 objecList,我必须排序,但是必须在结尾处为 null。 我的决定:

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

我的决定:

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

这将把一个 NULL值视为仅用于排序的 float.MaxValue,这将在列表的末尾放置空值,允许我们排除空值对 上升进行排序

这是我想出来的,因为我使用了扩展方法,而且我的条目是一个字符串,因此没有 .HasValue:

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

这个工作在内存中的 LINQ2对象。我没有测试它与 EF 或任何数据库 ORM。

我试图找到一个 LINQ 解决方案,但不能从这里的答案出来。

我的最终答案是:

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

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

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

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

e.g.: [null, "coconut", null, "apple", "strawberry"]
First sort: ["coconut", "apple", "strawberry", null, null]
Second sort: ["apple", "coconut", "strawberry", null, null]
因此,我更喜欢做一些更易读的东西,比如:
.OrderBy(f => f.SomeString ?? "z")

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

注意: 这不是一个终极解决方案,因为 "z"比像 zebra这样的 z 值先行。

更新 9/6/2016-About@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);

如果希望对 keySelector 的子属性进行排序,下面是检查 null 的扩展方法。

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

还有一种方法:

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

注意,在 Oracledb 中,r.SUP_APPROVED_IND.Trim() == null被视为 trim(SUP_APPROVED_IND) is null

详细信息请参见: 如何在实体框架中查询空值?

另一个选择(对我们的方案来说很方便) :

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

  • 用户应该按字母顺序排列
  • 根据他们的 ADName (但在用户列表的末尾) ,也没有 First-/LastName 的帐户
  • 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
1    AD\jon        Jon          Doe      | 5
5    AD\ama        Amanda       Whatever | 5
4    AD\Accounting null         null     | 10
3    AD\Support    null         null     | 10