2009-06-05 22 views
21

Sto creando un'origine dati fittizia su cui voglio essere in grado di passare in un elenco di SortExpressions.Come posso usare linq per ordinare per più campi?

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

Aggiornamento con il codice di Jon Skeet e anche l'intera classe. GetData() sta compilando l'oggetto con il numero x di record.

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

Non sembra che stia facendo nulla. Compilazioni, nessun errore, solo nessun tipo. Qualche idea?

risposta

27

Ci sono due problemi. Il primo è quello a cui altri hanno fatto riferimento: è necessario utilizzare il valore restituito da OrderBy ecc. Il secondo è che ogni volta che si chiama OrderBy, si aggiunge un nuovo ordinamento "primario". Vuoi veramente ThenBy dopo che il primo ordine è stato applicato. Questo lo rende piuttosto brutto, sfortunatamente. E 'ancora piuttosto brutto dopo un refactoring, ma non troppo male ...

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ì, questo è il motivo per cui sto facendo il ciclo for invece del foreach, perché stavo pensando che avevo bisogno di un ThenBy lì da qualche parte. – rball

+0

Ho appena risolto un bug, tra l'altro - hai bisogno della parte copyOfI altrimenti la variabile errata verrà catturata! –

+0

Crap, ancora non funziona. – rball

5

OrderBy restituisce una nuova IEnumerable, quindi è necessario fare qualcosa di simile:

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

Il lavoro OrderBy/OrderByDescending 'operatori' come String.ToUpper(), vale a dire, prendono la cosa si richiama su e produci una "copia" che ha ciò che hai chiesto.

In altre parole, invece di dire:

query.Orderby(item->item.X) 

si dovrebbe fare

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

o

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

[E come fa notare Jon Skeet, utilizzare ThenBy/ThenByDescending come nella sua risposta]

2

La query non è modificabile, quindi OrderBy restituisce un nuovo oggetto. È necessario effettuare la stessa chiamata, ma aggiungere "query =" all'inizio.

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

OrderBy on IEnumerable rendimenti restituisce un IOrderedEnumerable. Non li ordina in linea. Quindi prendi il valore di ritorno da .OrderBy e starai bene.

1

questo funzionerà:

YourCollection.Orderby(item => item.Property1).ThenBy(item => item.Property2); 
Problemi correlati