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 รายการ

คำตอบ:


213

คุณควรแน่นอนใช้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 ถึง Objects)

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

ซึ่งจะเทียบเท่ากับ

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

เนื่องจากลำดับการจัดเรียงมีเสถียรภาพ แต่คุณไม่ควร:

  • มันยากที่จะอ่าน
  • ทำงานได้ไม่ดี (เพราะเรียงลำดับใหม่ทั้งหมด)
  • อาจใช้ไม่ได้กับผู้ให้บริการรายอื่น (เช่น LINQ ถึง SQL)
  • โดยพื้นฐานแล้วไม่OrderByได้ออกแบบมาให้ใช้งานได้อย่างไร

ประเด็นOrderByคือการจัดเตรียมการฉายภาพการสั่งซื้อที่ "สำคัญที่สุด" จากนั้นใช้ThenBy(ซ้ำ ๆ ) เพื่อระบุการคาดการณ์ลำดับรองระดับอุดมศึกษาและอื่น ๆ

ลองคิดอย่างนี้อย่างมีประสิทธิภาพ: OrderBy(...).ThenBy(...).ThenBy(...)ช่วยให้คุณสร้างการเปรียบเทียบแบบผสมเดียวสำหรับวัตถุสองชิ้นใด ๆ จากนั้นเรียงลำดับตามลำดับเมื่อใช้การเปรียบเทียบแบบผสมนั้น นั่นคือสิ่งที่คุณต้องการอย่างแน่นอน


2
นั่นคือสิ่งที่ฉันคิด แต่ด้วยเหตุผลบางประการ OrderBy, ThenBy, ThenBy ดูเหมือนจะเรียงลำดับไม่ถูกต้องดังนั้นฉันจึงสงสัยว่าฉันใช้มันอย่างถูกต้องหรือไม่
DazManCat

14
โปรดทราบว่าในไวยากรณ์ของคิวรีคีย์เวิร์ดสำหรับการสั่งซื้อคือ orderby ไม่ใช่เรียงตาม ( ขอโทษสำหรับคนอวดรู้ - แค่อยากจะบอกว่าฉันเคยแก้ไขโพสต์ของ Jon Skeet แล้ว )
fostandy

1
จอนมีบางอย่างไม่เข้ากันสำหรับฉันจากส่วนแต่คุณไม่ควรอย่างยิ่ง (ซึ่งเกี่ยวข้องกับการใช้คำสั่งหลายคำสั่งโดยใช้ไวยากรณ์ linq fluent เนื่องจากแปลเป็น ThenBy ในข้อความค้นหาในท้องถิ่น): มันทำงานได้ไม่ดี (เพราะมัน เรียงลำดับใหม่ทั้งหมด) - คุณหมายถึงลำดับที่ 2 หรือ 3 โดยเรียงลำดับใหม่ทั้งหมดหรือไม่? ถ้าเป็นเช่นนั้นมันจะยังคงแปลเป็น ThenBy ได้อย่างไรหลังจากที่สั่งซื้อใหม่อีกครั้งโดยทิ้งลำดับก่อนหน้านี้
Veverke

@Veverke: มันเรียงลำดับลำดับทั้งหมดใหม่ แต่ในวิธีที่เสถียรดังนั้นหากค่าสองค่ามีค่า z เท่ากันการเรียงลำดับจะขึ้นอยู่กับ y แล้วตาม x
Jon Skeet

1
@Veverke: OrderBy(a).OrderBy(b).OrderBy(c)ยังคงใช้เอาต์พุตของการเรียงลำดับก่อนหน้าและจัดลำดับสิ่งทั้งหมดใหม่ แต่จะรักษาลำดับที่มีอยู่ (จากขั้นตอนก่อนหน้า) โดยที่สององค์ประกอบเท่ากันภายใต้การเปรียบเทียบใหม่ OrderBy(a).OrderBy(b)ลองนึกภาพเราก็มี ผลของการOrderBy(a)ในการเพิ่มการสั่งซื้อและแล้วเหล่านั้นจะถูกจัดลำดับใหม่ตามa bในผลสุดท้ายถ้าสองค่ามีเดียวกันbค่าที่พวกเขาจะได้รับการสั่งซื้อโดยaเกิดจากการที่มีเสถียรภาพการจัดเรียงเป็น - OrderBy(b).ThenBy(a)ดังนั้นจึงเทียบเท่ากับ
Jon Skeet

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

1

หากคุณต้องการจัดเรียงมากกว่าหนึ่งฟิลด์ให้ไปที่ ThenBy:

แบบนี้

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

0

ใช่คุณไม่ควรใช้ OrderBy หลายรายการหากคุณกำลังเล่นกับหลายปุ่ม ThenBy เป็นเดิมพันที่ปลอดภัยกว่าเนื่องจากจะดำเนินการหลังจาก OrderBy

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.