LINQ OrderBy so với ThenBy


123

Bất cứ ai có thể giải thích sự khác biệt giữa:

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

Đó là cách tiếp cận chính xác nếu tôi muốn đặt hàng theo 3 mục dữ liệu?

Câu trả lời:


212

Bạn chắc chắn nên sử dụng ThenByhơn là nhiều OrderBycuộc gọi.

Tôi muốn đề nghị điều này:

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

Lưu ý cách bạn có thể sử dụng cùng tên mỗi lần. Điều này cũng tương đương với:

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

Nếu bạn gọi OrderBynhiều lần, nó sẽ có hiệu quả sắp xếp lại trình tự hoàn toàn ba lần ... vì vậy các cuộc gọi cuối cùng sẽ có hiệu quả là người chiếm ưu thế. Bạn có thể (trong LINQ to Object) viết

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

tương đương với

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

vì thứ tự sắp xếp ổn định, nhưng bạn hoàn toàn không nên:

  • Thật khó đọc
  • Nó không hoạt động tốt (vì nó sắp xếp lại toàn bộ chuỗi)
  • Nó cũng có thể không hoạt động trong các nhà cung cấp khác (ví dụ LINQ to SQL)
  • Về cơ bản, nó không OrderByđược thiết kế để sử dụng.

Quan điểm OrderBylà cung cấp phép chiếu thứ tự "quan trọng nhất"; sau đó sử dụng ThenBy(lặp đi lặp lại) để chỉ định các phép chiếu thứ tự, bậc ba, vv.

Thực tế, hãy nghĩ về nó theo cách này: OrderBy(...).ThenBy(...).ThenBy(...)cho phép bạn xây dựng một so sánh tổng hợp duy nhất cho bất kỳ hai đối tượng nào, và sau đó sắp xếp chuỗi một lần bằng cách sử dụng so sánh tổng hợp đó. Đó gần như chắc chắn là những gì bạn muốn.


2
Đó là những gì tôi nghĩ, nhưng vì một số lý do OrderBy, ThenBy, ThenBy dường như không được sắp xếp chính xác nên tôi tự hỏi liệu tôi có sử dụng đúng không.
DazManCat

14
Lưu ý rằng trong cú pháp truy vấn, từ khóa để đặt hàng thực sự là thứ tự, không phải theo thứ tự. ( xin lỗi vì nhà sư phạm - chỉ muốn nói rằng tôi đã từng sửa một bài đăng của Jon Skeet )
fostandy

1
Jon, một cái gì đó không phù hợp với tôi từ phần nhưng bạn hoàn toàn không nên (liên quan đến việc áp dụng nhiều thứ tự bằng cách sử dụng cú pháp lưu loát linq vì nó dịch sang ThenBy, trong các truy vấn cục bộ): Nó không hoạt động tốt (vì nó sắp xếp lại toàn bộ chuỗi) - bạn có nghĩa là thứ 2 hoặc thứ 3 bằng cách sắp xếp lại toàn bộ chuỗi? nếu vậy, làm thế nào nó vẫn dịch sang ThenBy sau khi đã sắp xếp lại thứ tự loại bỏ thứ tự trước đó?
Veverke

@Veverke: Nó sắp xếp lại toàn bộ chuỗi, nhưng theo cách ổn định, vì vậy nếu hai giá trị có cùng giá trị z, thứ tự sẽ phụ thuộc vào y và sau đó vào x.
Jon Skeet

1
@Veverke: OrderBy(a).OrderBy(b).OrderBy(c)vẫn sử dụng đầu ra của loại trước và sắp xếp lại toàn bộ, nhưng nó giữ nguyên thứ tự hiện có (từ bước trước) trong đó hai yếu tố bằng nhau trong so sánh mới. Hãy tưởng tượng chúng ta chỉ có OrderBy(a).OrderBy(b). Kết quả OrderBy(a)là theo athứ tự tăng dần , và sau đó những người được sắp xếp lại theo b. Trong kết quả cuối cùng, nếu hai giá trị có cùng bgiá trị, chúng sẽ được sắp xếp theo thứ tự ado loại ổn định - vì vậy nó tương đương với OrderBy(b).ThenBy(a).
Jon Skeet

2

Tôi thấy sự khác biệt này gây khó chịu khi cố gắng xây dựng các truy vấn theo cách chung chung, vì vậy tôi đã tạo một người trợ giúp nhỏ để tạo OrderBy / ThenBy theo thứ tự phù hợp, bao nhiêu tùy thích.

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

Có rất nhiều cách bạn có thể sử dụng tùy thuộc vào trường hợp sử dụng của mình, nhưng nếu bạn đã thông qua một danh sách các cột và hướng sắp xếp dưới dạng chuỗi và bool, bạn có thể lặp qua chúng và sử dụng chúng trong một công tắc như:

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;

Kết quả trong sortedQueryđược sắp xếp theo thứ tự mong muốn, thay vì dùng đến nhiều lần như câu trả lời khác ở đây cảnh báo.


1
Hoặc chỉ một số phương thức mở rộng stackoverflow.com/a/45486019/1300910
huysentbeanw

1

nếu bạn muốn sắp xếp nhiều hơn một trường thì hãy truy cập ThenBy:

như thế này

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

0

Có, bạn không bao giờ nên sử dụng nhiều OrderBy nếu bạn đang chơi với nhiều phím. ThenBy an toàn hơn vì nó sẽ thực hiện sau OrderBy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.