LINQ OrderBy与ThenBy


123

谁能解释一下两者之间的区别:

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项数据排序,哪种方法正确?

Answers:


212

绝对应该使用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多次,这将有效地重新排序的序列完全 三次...所以最后调用将有效地占主导地位。您可以(在LINQ to Objects中)编写

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

相当于

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

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

  • 很难看
  • 效果不好(因为它会重新排序整个序列)
  • 它可能无法在其他提供程序中运行(例如LINQ to SQL)
  • 基本上不是OrderBy设计用途。

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

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


2
那就是我的想法,但是由于某种原因,OrderBy,ThenBy,ThenBy似乎无法正确排序,因此我想知道自己是否正确使用了它。
DazManCat

14
请注意,在查询语法中,用于排序的关键字实际上是orderby,而不是order by。(抱歉,我的
脚手架

1
乔恩(Jon),但我绝对不适合某些东西,但您绝对不应在本节中提到(这与使用linq流利语法应用多顺序bys有关,因为在本地查询中它会转换为ThenBy):它的效果不佳(因为它对整个序列进行重新排序) -您是通过对整个序列进行重新排序来表示2阶还是3阶?如果是这样,在对序列重新排序后丢弃先前的排序后,它将如何转换为ThenBy?
Veverke '16

@Veverke:它以稳定的方式对整个序列进行重新排序,因此,如果两个值具有相同的z值,则排序将取决于y,然后取决于x。
乔恩·斯基特

1
@Veverke:OrderBy(a).OrderBy(b).OrderBy(c)仍使用先前排序的输出,并对整个事物重新排序,但是它保留了现有顺序(来自上一步),其中在新比较下两个元素相等。想象我们刚刚拥有OrderBy(a).OrderBy(b)。的结果OrderBy(a)按升序a排列,然后根据进行重新排序b。在最终结果中,如果两个值具有相同的b值,则a由于排序是稳定的,因此将按它们排序-等效于OrderBy(b).ThenBy(a)
乔恩·斯基特

2

在尝试以通用方式构建查询时,我发现这种区别很烦人,因此我提供了一个小帮手,可以按任意顺序以适当的顺序生成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所需的顺序排序,而不是一遍又一遍地求助于此处警告的其他答案。


1
或者只是一些扩展方法stackoverflow.com/a/45486019/1300910
huysentruitw


0

是的,如果您正在使用多个键,则永远不要使用多个OrderBy。然后,按个数比较安全,因为它将在按个数之后执行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.