LINQ OrderBy 对 ThenBy

谁能解释一下:

tmp = invoices.InvoiceCollection
.OrderBy(sort1 => sort1.InvoiceOwner.LastName)
.OrderBy(sort2 => sort2.InvoiceOwner.FirstName)
.OrderBy(sort3 => sort3.InvoiceID);

还有

tmp = invoices.InvoiceCollection
.OrderBy(sort1 => sort1.InvoiceOwner.LastName)
.ThenBy(sort2 => sort2.InvoiceOwner.FirstName)
.ThenBy(sort3 => sort3.InvoiceID);

如果我想订购3项数据,哪种方法是正确的?

47623 次浏览

您应该 当然使用 ThenBy而不是多个 OrderBy调用。

我的建议是:

tmp = invoices.InvoiceCollection
.OrderBy(o => o.InvoiceOwner.LastName)
.ThenBy(o => o.InvoiceOwner.FirstName)
.ThenBy(o => o.InvoiceID);

请注意如何使用相同的名称每次。这也相当于:

tmp = from o in invoices.InvoiceCollection
orderby o.InvoiceOwner.LastName,
o.InvoiceOwner.FirstName,
o.InvoiceID
select o;

如果你调用 OrderBy多次,它将有效地重新排序序列 彻底的三次... 所以最后的调用将有效地占主导地位。你 可以(在 LINQtoObjects 中)写

foo.OrderBy(x).OrderBy(y).OrderBy(z)

这相当于

foo.OrderBy(z).ThenBy(y).ThenBy(x)

因为排序顺序是稳定的,但是绝对不应该:

  • 很难读懂
  • 它不能很好地执行(因为它重新排序了整个序列)
  • 它可以在其他提供程序(例如 LINQ to SQL)中很好地工作
  • 这基本上不是 OrderBy的设计初衷。

OrderBy的要点是提供“最重要的”订单投影,然后使用 ThenBy(重复)指定二级、三级等订单投影。

实际上,可以这样想: OrderBy(...).ThenBy(...).ThenBy(...)允许您为任意两个对象构建一个复合比较,然后使用该复合比较对序列 一次进行排序。这几乎就是你想要的。

在尝试以通用方式构建查询时,我发现这种区别很烦人,因此我制作了一个小助手,按照正确的顺序生成 OrderBy/ThenBy,可以按照您的喜好生成任意类型的查询。

public class EFSortHelper
{
public static EFSortHelper<TModel> Create<TModel>(IQueryable<T> query)
{
return new EFSortHelper<TModel>(query);
}
}


public class EFSortHelper<TModel> : EFSortHelper
{
protected IQueryable<TModel> unsorted;
protected IOrderedQueryable<TModel> sorted;


public EFSortHelper(IQueryable<TModel> unsorted)
{
this.unsorted = unsorted;
}


public void SortBy<TCol>(Expression<Func<TModel, TCol>> sort, bool isDesc = false)
{
if (sorted == null)
{
sorted = isDesc ? unsorted.OrderByDescending(sort) : unsorted.OrderBy(sort);
unsorted = null;
}
else
{
sorted = isDesc ? sorted.ThenByDescending(sort) : sorted.ThenBy(sort)
}
}


public IOrderedQueryable<TModel> Sorted
{
get
{
return sorted;
}
}
}

根据你的用例,有很多方法可以使用它,但是如果你被传递一个排序列和方向的列表作为字符串和布尔,你可以循环使用它们,并在一个开关中使用它们,比如:

var query = db.People.AsNoTracking();
var sortHelper = EFSortHelper.Create(query);
foreach(var sort in sorts)
{
switch(sort.ColumnName)
{
case "Id":
sortHelper.SortBy(p => p.Id, sort.IsDesc);
break;
case "Name":
sortHelper.SortBy(p => p.Name, sort.IsDesc);
break;
// etc
}
}


var sortedQuery = sortHelper.Sorted;

sortedQuery中的结果按照所需的顺序进行排序,而不是像这里的另一个答案所警告的那样一遍又一遍地重复。

如果您想对多个字段进行排序,那么可以选择 ThenBy:

像这样

list.OrderBy(personLast => person.LastName)
.ThenBy(personFirst => person.FirstName)

是的,如果使用多个键,则永远不应使用多个 OrderBy。 由于将在 OrderBy 之后执行,因此使用 then By 更安全。