2009-06-05 10 views
21

Je crée une source de données fictive que je veux pouvoir transmettre dans une liste de SortExpressions.Comment utiliser linq pour trier plusieurs champs?

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

Mise à jour avec le code de Jon Skeet et aussi toute la classe. GetData() remplit simplement l'objet avec x nombre d'enregistrements.

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

Ne semble pas faire quoi que ce soit. Compile, pas d'erreur, juste pas de tri. Des idées?

Répondre

27

Il y a deux problèmes. Le premier est celui auquel d'autres ont fait allusion - vous devez utiliser la valeur renvoyée par OrderBy etc. La seconde est que chaque fois que vous appelez OrderBy, cela ajoute un nouveau classement "primaire". Vous voulez vraiment ThenBy après la première commande a été appliquée. Cela le rend plutôt moche, malheureusement. Il est encore assez laid après un refactoring, mais pas trop 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

Ouais, c'est la raison pour laquelle je fais la boucle au lieu du foreach, parce que je pensais que j'avais besoin d'un ThenBy là-bas quelque part. – rball

+0

Je viens de corriger un bug, d'ailleurs - vous avez besoin de la partie copyOfI sinon la mauvaise variable sera capturée! –

+0

Crap, ne fonctionne toujours pas. – rball

5

OrderBy retourne une nouvelle IEnumerable, vous devez donc faire quelque chose comme:

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

Le OrderBy/travail OrderByDescending 'opérateurs comme string.toupper(), par exemple, ils prennent la chose que vous invoquez sur , et donnez une «copie» qui a ce que vous avez demandé.

En d'autres termes, au lieu de dire:

query.Orderby(item->item.X) 

vous devriez faire

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

ou

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

[Et comme le souligne Jon Skeet sur, utilisez ThenBy/ThenByDescending comme dans sa réponse]

2

La requête n'est pas modifiable, donc OrderBy retourne un nouvel objet. Vous devez faire le même appel, mais ajoutez "query =" au début.

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

OrderBy on IEnumerable retours retourne un IOrderedEnumerable. Cela ne les range pas en ligne. Alors, obtenez la valeur de retour de votre .OrderBy et tout ira bien.

1

Cela fonctionne:

YourCollection.Orderby(item => item.Property1).ThenBy(item => item.Property2); 
Questions connexes