LINQ OrderBy ve ThenBy


123

Aradaki farkın ne olduğunu herkes açıklayabilir:

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

ve

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

3 adet veriye göre sıralamak istersem doğru yaklaşım hangisidir?

Yanıtlar:


213

Sen gerektiğini kesinlikle kullanmayın ThenBybirden ziyade OrderByaramalar.

Bunu öneririm:

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

Her seferinde aynı adı nasıl kullanabileceğinizi not edin. Bu aynı zamanda şuna da eşdeğerdir:

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

Birden OrderByçok kez ararsanız , diziyi tamamen üç kez etkili bir şekilde yeniden düzenler ... böylece son arama etkili bir şekilde baskın olan olur. Sen can (LINQ Nesneler) yazma

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

eşdeğer olacaktır

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

sıralama düzeni sabit olduğundan, ancak şunları kesinlikle yapmamalısınız:

  • Okumak zor
  • İyi performans göstermiyor (çünkü tüm diziyi yeniden sıraladı)
  • İyi olabilir değil diğer sağlayıcılar (örneğin LINQ to SQL) çalışmak
  • Temelde nasıl OrderBykullanılmak üzere tasarlandığı değil .

Buradaki OrderByamaç, "en önemli" sipariş projeksiyonunu sağlamaktır; daha sonra ThenByikincil, üçüncül vb sıralama projeksiyonlarını belirtmek için (tekrar tekrar) kullanın.

: Etkili, Şöyle düşünmek OrderBy(...).ThenBy(...).ThenBy(...)tür ardından dizisini herhangi iki nesne için tek kompozit karşılaştırma oluşturmanızı sağlar ve bir kez bu kompozit karşılaştırma kullanılarak. Neredeyse kesinlikle istediğiniz şey bu.


2
Düşündüğüm buydu ama bir nedenden dolayı OrderBy, ThenBy, ThenBy doğru sıralanıyor gibi görünmüyor, bu yüzden doğru kullanıp kullanmadığımı merak ettim.
DazManCat

14
Sorgu sözdiziminde sıralama için anahtar sözcüğün sıralama değil, sıralama olduğunu unutmayın. ( bilgiçlik için özür dilerim - bir keresinde bir Jon Skeet gönderisini düzelttiğimi söylemek istedim )
fostandy

1
Jon, benim için kesinlikle uymuyor ama bölümden bir şey birbirine uymuyor (bu, yerel sorgularda ThenBy'ye çevrildiği için linq fluent sözdizimini kullanarak birden fazla sipariş uygulamakla ilgili): İyi performans göstermiyor (çünkü tüm diziyi yeniden sıralar) - tüm diziyi yeniden sıralayarak 2. veya 3. sırayı mı kastediyorsunuz? eğer öyleyse, önceki sıralamayı atarak sırayı yeniden sipariş ettikten sonra ThenBy'ye nasıl çevrilecek?
Veverke

@Veverke: Tüm diziyi yeniden sıralar, ancak kararlı bir şekilde, yani iki değer aynı z değerine sahipse, sıralama y'ye ve sonra x'e bağlı olacaktır.
Jon Skeet

1
@Veverke: OrderBy(a).OrderBy(b).OrderBy(c)önceki sıralamanın çıktısını kullanmaya devam eder ve her şeyi yeniden sıralar, ancak yeni karşılaştırmada iki öğenin eşit olduğu mevcut sırayı (önceki adımdan itibaren) korur. Sadece sahip olduğumuzu hayal edin OrderBy(a).OrderBy(b). Sonuçları OrderBy(a)artan asıradadır ve daha sonra bunlara göre yeniden sıralanır b. Nihai sonuçta, iki değer aynı bdeğere sahipse a, sıralamanın kararlı olması nedeniyle sıralanacaklar - yani eşdeğerdir OrderBy(b).ThenBy(a).
Jon Skeet

2

Sorguları genel bir şekilde oluşturmaya çalışırken bu ayrımı can sıkıcı buldum, bu yüzden istediğiniz kadar çok tür için uygun sırada OrderBy / ThenBy üretmek için küçük bir yardımcı yaptım.

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

Kullanım durumunuza bağlı olarak bunu kullanmanın birçok yolu vardır, ancak örneğin dizeler ve bool'lar olarak sıralama sütunları ve yönleri listesi geçirdiyseniz, bunların üzerinden döngü yapabilir ve aşağıdaki gibi bir anahtarla kullanabilirsiniz:

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;

Buradaki sortedQuerydiğer yanıtın uyarıda bulunduğu gibi, sonuç, tekrar tekrar başvurmak yerine istenen sırada sıralanır.


1
Ya da sadece bazı uzatma yöntemlerinin stackoverflow.com/a/45486019/1300910
huysentruitw

1

birden fazla alanı sıralamak istiyorsanız ThenBy'ye gidin:

bunun gibi

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

0

Evet, birden fazla tuşla oynuyorsanız asla birden fazla OrderBy kullanmamalısınız. ThenBy, OrderBy'den sonra performans göstereceği için daha güvenlidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.