2010-09-21 4 views
97

Puede alguien explicar cuál es la diferencia entre:LINQ OrdenarPor frente ThenBy

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

y

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

¿Cuál es el enfoque correcto si lo deseo a la orden por 3 elementos de datos?

Respuesta

171

Usted debe definitivamente utilizar ThenBy en lugar de varias OrderBy llamadas. (Supongo que uno de los fragmentos en su pregunta pretendía utilizar ThenBy En el momento de escribir estas líneas, los dos fragmentos son idénticos..)

Yo sugeriría esto:

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

Nota cómo se puede usa el mismo nombre cada vez Esto también es equivalente a:

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

Si llama OrderBy varias veces, se reordenará de manera efectiva la secuencia completamente tres veces ... por lo que la decisión final será efectiva el dominante. Usted puede (en LINQ a Objetos) escribe

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

lo que equivaldría a

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

como el orden de clasificación es estable, pero a pesar de todo no deben:

  • Es difícil de leer
  • No funciona bien (porque reordena toda la secuencia)
  • Puede bien no trabajar en otros proveedores (p. LINQ to SQL)
  • Básicamente no es cómo OrderBy fue diseñado para ser utilizado.

El objetivo de OrderBy es proporcionar la proyección de pedido "más importante"; luego use ThenBy (repetidamente) para especificar proyecciones de ordenamiento secundarias, terciarias, etc.

Efectivamente, creo que de esta manera: OrderBy(...).ThenBy(...).ThenBy(...) le permite construir una única comparación compuesta por dos objetos, y luego ordenar la secuencia de una vez que la comparación utilizando compuesta. Eso es casi seguro lo que quieres.

+2

Eso es lo que pensé, pero, por alguna razón, el OrderBy, ThenBy, ThenBy no parece estar ordenando correctamente, así que me preguntaba si lo estaba usando correctamente. – DazManCat

+1

+1 para la parte 'Esto también es equivalente a' que estaba buscando. –

+10

Tenga en cuenta que en la sintaxis de la consulta, la palabra clave para ordenar es realmente orderby, no order by. (Lo siento por la pedantería, solo quería decir que una vez corregí una publicación de Jon Skeet) – fostandy

2

Encontré esta distinción molesta al tratar de crear consultas de manera genérica, así que hice un pequeño ayudante para producir OrderBy/ThenBy en el orden correcto, para tantos géneros como desee.

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

Hay un montón de maneras que usted puede utilizar esta función en su caso de uso, pero si usted fuera, por ejemplo, aprobó una lista de columnas de orden y las direcciones como cuerdas y Bools, podrías bucle sobre ellas y las usan en un interruptor como:

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; 

el resultado en sortedQuery se ordena en el orden deseado, en lugar de recurrir una y otra como la otra respuesta aquí advierte.

+0

O solo algunos métodos de extensión https://stackoverflow.com/a/45486019/1300910 –

0

si desea ordenar más de un campo y luego ir por ThenBy:

como esto

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

Sí, nunca se debe utilizar múltiples OrdenarPor si está jugando con varias claves. ThenBy es una apuesta más segura ya que funcionará después de OrderBy.

Cuestiones relacionadas