2009-06-05 17 views
21

Estoy creando una fuente de datos simulada que deseo poder pasar en una lista de SortExpressions.¿Cómo puedo usar linq para ordenar por varios campos?

public SortExpression(string name, SortDirection direction) 
{ 
    this.name = name; 
    this.direction = direction; 
} 

actualización con código de Jon Skeet y también a toda la clase. GetData() solo está llenando el objeto con x cantidad de registros.

public class Data 
{ 

public int Id { get; set; } 
public Guid gId { get; set; } 
public string Name { get; set; } 
public string Phone { get; set; } 
public string Address { get; set; } 
public DateTime Created { get; set; } 
public string SortMe { get; set; } 
public static List<Data> GetFakeData(int start, int numberToFetch, IList<SortExpression> sortExpressions, IList<FilterExpression> filterExpressions, out int totalRecords) 
{ 
    DataCollection items = GetData(); 
    IEnumerable<Data> query = from item in items select item; 

    bool sortExpressionsExist = sortExpressions != null; 
    if (sortExpressionsExist) 
    { 
     // Won't be read in the first iteration; will be written to 
     IOrderedEnumerable<Data> orderedQuery = null; 
     for (int i = 0; i < sortExpressions.Count; i++) 
     { 
      // Avoid single variable being captured: capture one per iteration. 
      // Evil bug which would be really hard to find :) 
      int copyOfI = i; 
      // Tailor "object" depending on what GetProperty returns. 
      Func<Data, object> expression = item => 
        item.GetType().GetProperty(sortExpressions[copyOfI].Name); 

      if (sortExpressions[i].Direction == SortDirection.Ascending) 
      { 
       orderedQuery = (i == 0) ? query.OrderBy(expression) 
             : orderedQuery.ThenBy(expression); 
      } 
      else 
      { 
       orderedQuery = (i == 0) ? query.OrderByDescending(expression) 
             : orderedQuery.ThenByDescending(expression); 
      } 
     } 
     query = orderedQuery; 
    } 

    bool filterExpressionsExist = filterExpressions != null; 
    if (filterExpressionsExist) 
    { 
     foreach (var filterExpression in filterExpressions) 
     { 
      query.Where(item => item.GetType().GetProperty(filterExpression.ColumnName).GetValue(item, null).ToString().Contains(filterExpression.Text)); 
     } 
    } 
    totalRecords = query.Count(); 


     return query.Skip(start).Take(numberToFetch).ToList<Data>(); 
    } 
} 

Parece que no hace nada. Compila, sin errores, simplemente no ordena. ¿Algunas ideas?

Respuesta

27

Hay dos problemas. El primero es al que han aludido otros: debe usar el valor devuelto por OrderBy, etc. El segundo es que cada vez que llame al OrderBy, eso agregará un nuevo pedido "primario". Realmente desea ThenBy después de que se haya aplicado el primer pedido. Eso lo hace bastante feo, por desgracia. Todavía es bastante feo después de una refactorización, pero no demasiado mal ...

IEnumerable<Data> query = from item in items select item; 
if (sortExpressionsExist) 
{ 
    // Won't be read in the first iteration; will be written to 
    IOrderedEnumerable<Data> orderedQuery = null; 
    for (int i = 0; i < sortExpressions.Count; i++) 
    { 
     // Avoid single variable being captured: capture one per iteration. 
     // Evil bug which would be really hard to find :) 
     int copyOfI = i; 
     // Tailor "object" depending on what GetProperty returns. 
     Func<Data, object> expression = item => 
       item.GetType() 
        .GetProperty(sortExpressions[copyOfI].Name) 
        .GetValue(item, null); 

     if (sortExpressions[i].Direction == SortDirection.Ascending) 
     { 
      orderedQuery = (i == 0) ? query.OrderBy(expression) 
            : orderedQuery.ThenBy(expression); 
     } 
     else 
     { 
      orderedQuery = (i == 0) ? query.OrderByDescending(expression) 
            : orderedQuery.ThenByDescending(expression); 
     } 
    } 
    query = orderedQuery; 
} 
+0

Sí, es por eso que estoy haciendo el bucle en lugar del foreach, porque estaba pensando que necesitaba un ThenBy en algún lugar. – rball

+0

Acabo de corregir un error, por cierto, necesitas la parte copyOfI ya que de lo contrario se capturará la variable incorrecta. –

+0

Mierda, todavía no está funcionando. – rball

5

OrdenarPor devuelve un nuevo objeto IEnumerable, por lo que tiene que hacer algo como:

IEnumerable<Data> results 
    = query.OrderBy(item => item.GetType().GetProperty(sortExpressions[i].Name)); 
2

trabajo '' La operadores OrdenarPor/OrderByDescending como string.toupper(), es decir, toman la cosa que lo invocan en , y ceda una 'copia' que tenga lo que usted pidió.

En otras palabras, en lugar de decir:

query.Orderby(item->item.X) 

que debe hacer

query = query.Orderby(item->item.X) 

o

sortedResult = query.Orderby(item->item.X) 

[Y como señala Jon Skeet, utilice ThenBy/ThenByDescending como en su respuesta]

2

La consulta no es mutable, por lo que OrderBy devuelve un nuevo objeto. Debe hacer la misma llamada, pero agregue "query =" al principio.

query = query.OrderBy(item => item.GetType().GetProperty(sortExpressions[i].Name)); 
2

OrderBy on IEnumerable vuelve devuelve un IOrderedEnumerable. No los clasifica en línea. Así que obtenga el valor de retorno de su .OrderBy y estará bien.

1

esto funcionará:

YourCollection.Orderby(item => item.Property1).ThenBy(item => item.Property2); 
Cuestiones relacionadas