LINQ OrderBy versus ThenBy


123

Adakah yang bisa menjelaskan apa perbedaan antara:

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

dan

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

Manakah pendekatan yang benar jika saya ingin memesan berdasarkan 3 item data?

Jawaban:


213

Anda pasti harus menggunakan ThenBydaripada beberapa OrderBypanggilan.

Saya menyarankan ini:

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

Perhatikan bagaimana Anda dapat menggunakan nama yang sama setiap saat. Ini juga setara dengan:

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

Jika Anda menelepon OrderBybeberapa kali, itu akan secara efektif menyusun ulang urutan seluruhnya tiga kali ... jadi panggilan terakhir secara efektif akan menjadi yang dominan. Anda dapat (dalam LINQ ke Objek) menulis

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

yang setara dengan

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

karena urutan sortirnya stabil, tetapi Anda sama sekali tidak boleh:

  • Sulit untuk dibaca
  • Tidak bekerja dengan baik (karena menyusun ulang seluruh urutan)
  • Ini mungkin tidak berfungsi di penyedia lain (misalnya LINQ ke SQL)
  • Ini pada dasarnya bukan bagaimana OrderBydirancang untuk digunakan.

Intinya OrderByadalah untuk memberikan proyeksi pemesanan yang "paling penting"; kemudian gunakan ThenBy(berulang kali) untuk menentukan proyeksi urutan sekunder, tersier, dll.

Secara efektif, pikirkan seperti ini: OrderBy(...).ThenBy(...).ThenBy(...)memungkinkan Anda membuat perbandingan gabungan tunggal untuk dua objek, lalu mengurutkan urutan sekali menggunakan perbandingan gabungan tersebut. Hampir pasti itulah yang Anda inginkan.


2
Itulah yang saya pikirkan tetapi, untuk beberapa alasan OrderBy, ThenBy, ThenBy tampaknya tidak menyortir dengan benar jadi saya bertanya-tanya apakah saya menggunakannya dengan benar.
DazManCat

14
Perhatikan bahwa dalam sintaks kueri, kata kunci untuk pengurutan sebenarnya adalah orderby, bukan urut. ( maaf untuk kelayakannya - hanya ingin mengatakan saya pernah mengoreksi posting Jon Skeet )
fostandy

1
Jon, ada sesuatu yang tidak cocok untuk saya dari bagian tetapi Anda sama sekali tidak boleh (yang berhubungan dengan menerapkan beberapa urutan dengan menggunakan sintaks linq fluent karena diterjemahkan ke ThenBy, dalam kueri lokal): Tidak berkinerja baik (karena itu menyusun ulang seluruh urutan) - maksud Anda urutan ke-2 atau ke-3 dengan menyusun ulang seluruh urutan? jika demikian, bagaimana ini akan tetap diterjemahkan ke ThenBy setelah mengurutkan ulang urutan membuang pengurutan sebelumnya?
Veverke

@Veverke: Ini mengurutkan ulang seluruh urutan, tetapi dengan cara yang stabil, jadi jika dua nilai memiliki nilai z yang sama, urutannya akan bergantung pada y dan kemudian pada x.
Jon Skeet

1
@Veverke: OrderBy(a).OrderBy(b).OrderBy(c)masih menggunakan keluaran dari pengurutan sebelumnya, dan menyusun ulang semuanya, tetapi mempertahankan urutan yang ada (dari langkah sebelumnya) di mana dua elemen sama di bawah perbandingan baru. Bayangkan kita baru saja OrderBy(a).OrderBy(b). Hasil dari OrderBy(a)berada di aurutan yang meningkat , dan kemudian diurutkan kembali sesuai dengan b. Pada hasil akhir, jika dua nilai memiliki nilai yang sama b, mereka akan diurutkan berdasarkan akarena pengurutannya stabil - jadi setara dengan OrderBy(b).ThenBy(a).
Jon Skeet

2

Saya menemukan perbedaan ini menjengkelkan saat mencoba membangun kueri secara umum, jadi saya membuat sedikit bantuan untuk menghasilkan OrderBy / ThenBy dalam urutan yang benar, untuk sebanyak mungkin jenis yang Anda suka.

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

Ada banyak cara Anda dapat menggunakan ini tergantung pada kasus penggunaan Anda, tetapi jika Anda misalnya memberikan daftar kolom sortir dan arah sebagai string dan bools, Anda dapat mengulanginya dan menggunakannya dalam sakelar seperti:

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;

Hasil di sortedQuerydiurutkan dalam urutan yang diinginkan, alih-alih menggunakan berulang-ulang seperti yang diperingatkan oleh jawaban lain di sini.


1
Atau hanya beberapa metode ekstensi stackoverflow.com/a/45486019/1300910
huysentruitw

1

jika Anda ingin mengurutkan lebih dari satu bidang, pilih ThenBy:

seperti ini

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

0

Ya, Anda tidak boleh menggunakan banyak OrderBy jika Anda bermain dengan banyak tombol. ThenBy adalah taruhan yang lebih aman karena akan dilakukan setelah OrderBy.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.