web-dev-qa-db-fra.com

ascendant/descendant dans LINQ - peut-on changer l'ordre via paramètre?

J'ai une méthode qui reçoit le paramètre "bool sortAscending". Maintenant, je veux utiliser LINQ pour créer une liste triée en fonction de ce paramètre. J'ai eu alors ceci:

var ascendingQuery = from data in dataList
                      orderby data.Property ascending
                      select data;

var descendingQuery = from data in dataList
                      orderby data.Property descending
                      select data;

Comme vous pouvez le constater, les deux requêtes ne diffèrent que par "ascendant" resp. "descendant". J'aimerais fusionner les deux requêtes, mais je ne sais pas comment. Est-ce que quelqu'un a la réponse?

69
Johannes

Vous pouvez facilement créer votre propre méthode d’extension sur IEnumerable ou IQueryable:

public static IOrderedEnumerable<TSource> OrderByWithDirection<TSource,TKey>
    (this IEnumerable<TSource> source,
     Func<TSource, TKey> keySelector,
     bool descending)
{
    return descending ? source.OrderByDescending(keySelector)
                      : source.OrderBy(keySelector);
}

public static IOrderedQueryable<TSource> OrderByWithDirection<TSource,TKey>
    (this IQueryable<TSource> source,
     Expression<Func<TSource, TKey>> keySelector,
     bool descending)
{
    return descending ? source.OrderByDescending(keySelector)
                      : source.OrderBy(keySelector);
}

Oui, vous perdez la possibilité d'utiliser une expression de requête ici - mais franchement, je ne pense pas que vous tiriez réellement profit d'une expression de requête de toute façon dans ce cas. Les expressions de requête conviennent parfaitement aux choses complexes, mais si vous ne faites qu'une seule opération, il est plus simple de simplement mettre cette opération:

var query = dataList.OrderByWithDirection(x => x.Property, direction);
106
Jon Skeet

En termes de mise en œuvre, cela change la méthode - de OrderBy/ThenBy à OrderByDescending/ThenByDescending. Cependant, vous pouvez appliquer le tri séparément à la requête principale ...

var qry = from .... // or just dataList.AsEnumerable()/AsQueryable()

if(sortAscending) {
    qry = qry.OrderBy(x=>x.Property);
} else {
    qry = qry.OrderByDescending(x=>x.Property);
}

Toute utilisation? Vous pouvez créer l'intégralité de la "commande" de manière dynamique, mais elle est plus complexe ...

Une autre astuce (principalement appropriée à LINQ-to-Objects) consiste à utiliser un multiplicateur, de -1/1. Cela n’est vraiment utile que pour les données numériques, mais c’est un moyen effronté d’atteindre le même résultat.

41
Marc Gravell

Qu'en est-il de commander desc par la propriété désirée,

   blah = blah.OrderByDescending(x => x.Property);

Et puis faire quelque chose comme

  if (!descending)
  {
       blah = blah.Reverse()
  }
  else
  {
      // Already sorted desc ;)
  }

Est-ce que c'est Reverse () trop lent?

6
sports

En plus de la belle solution donnée par @Jon Skeet, j'avais également besoin de ThenBy et ThenByDescending, je l'ajoute donc en fonction de sa solution:

    public static IOrderedEnumerable<TSource> ThenByWithDirection<TSource, TKey>(
         this IOrderedEnumerable<TSource> source, 
         Func<TSource, TKey> keySelector,  
         bool descending)
    {
        return descending ? 
               source.ThenByDescending(keySelector) :
               source.ThenBy(keySelector);
    }
0
ehh