web-dev-qa-db-fra.com

Comment puis-je faire un OrderBy avec un paramètre de chaîne dynamique?

Je veux faire ça:

var orderBy = "Nome, Cognome desc";

var timb = time.Timbratures.Include("Anagrafica_Dipendente")
    .Where(p => p.CodDipendente == 1);

if(orderBy != "")
    timb = timb.OrderBy(orderBy);

Y a-t-il une surcharge OrderBy disponible qui accepte un paramètre de chaîne?

52
Luca Romagnoli

Absolument. Vous pouvez utiliser la bibliothèque de requêtes dynamiques LINQ, trouvée sur le blog de Scott Guthrie . Il existe également une version mise à jour disponible sur CodePlex .

Il vous permet de créer des clauses OrderBy, Where clauses, et à peu près tout le reste en passant des paramètres de chaîne. Cela fonctionne très bien pour créer du code générique pour les grilles de tri/filtrage, etc.

var result = data
    .Where(/* ... */)
    .Select(/* ... */)
    .OrderBy("Foo asc");

var query = DbContext.Data
    .Where(/* ... */)
    .Select(/* ... */)
    .OrderBy("Foo ascending");
41
Mike Mooney

Si vous utilisez des LINQ-to-objects simples et que vous ne voulez pas dépendre d'une bibliothèque externe, il n'est pas difficile d'obtenir ce que vous voulez.

La clause OrderBy() accepte un Func<TSource, TKey> Qui obtient une clé de tri à partir d'un élément source. Vous pouvez définir la fonction en dehors de la clause OrderBy():

Func<Item, Object> orderByFunc = null;

Vous pouvez ensuite l'affecter à différentes valeurs en fonction des critères de tri:

if (sortOrder == SortOrder.SortByName)
  orderByFunc = item => item.Name;
else if (sortOrder == SortOrder.SortByRank)
  orderByFunc = item => item.Rank;

Ensuite, vous pouvez trier:

var sortedItems = items.OrderBy(orderByFunc);

Cet exemple suppose que le type de source est Item qui possède les propriétés Name et Rank.

Notez que dans cet exemple TKey est Object pour ne pas contraindre les types de propriétés qui peuvent être triés. Si la fonction renvoie un type de valeur (comme Int32), Elle sera encadrée lors du tri et cela est quelque peu inefficace. Si vous pouvez contraindre TKey à un type de valeur spécifique, vous pouvez contourner ce problème.

63

Une autre solution de codeConcussion ( https://stackoverflow.com/a/7265394/2793768 )

var param = "Address";    
var pi = typeof(Student).GetProperty(param);    
var orderByAddress = items.OrderBy(x => pi.GetValue(x, null));
29
Chinh Phan

Vous n'avez pas besoin d'une bibliothèque externe pour cela. Le code ci-dessous fonctionne pour LINQ to SQL/entités.

    /// <summary>
    /// Sorts the elements of a sequence according to a key and the sort order.
    /// </summary>
    /// <typeparam name="TSource">The type of the elements of <paramref name="query" />.</typeparam>
    /// <param name="query">A sequence of values to order.</param>
    /// <param name="key">Name of the property of <see cref="TSource"/> by which to sort the elements.</param>
    /// <param name="ascending">True for ascending order, false for descending order.</param>
    /// <returns>An <see cref="T:System.Linq.IOrderedQueryable`1" /> whose elements are sorted according to a key and sort order.</returns>
    public static IQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> query, string key, bool ascending = true)
    {
        if (string.IsNullOrWhiteSpace(key))
        {
            return query;
        }

        var lambda = (dynamic)CreateExpression(typeof(TSource), key);

        return ascending 
            ? Queryable.OrderBy(query, lambda) 
            : Queryable.OrderByDescending(query, lambda);
    }

    private static LambdaExpression CreateExpression(Type type, string propertyName)
    {
        var param = Expression.Parameter(type, "x");

        Expression body = param;
        foreach (var member in propertyName.Split('.'))
        {
            body = Expression.PropertyOrField(body, member);
        }

        return Expression.Lambda(body, param);
    }

(CreateExpression copié de https://stackoverflow.com/a/16208620/111438 )

14
niaher

La solution la plus simple et la meilleure:

mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s));
8
Kasper Roma

Regardez ce blog ici . Il décrit un moyen de le faire, en définissant un EntitySorter<T>.

Il vous permet de passer un IEntitySorter<T> dans vos méthodes de service et utilisez-le comme ceci:

public static Person[] GetAllPersons(IEntitySorter<Person> sorter)
{
    using (var db = ContextFactory.CreateContext())
    {
        IOrderedQueryable<Person> sortedList = sorter.Sort(db.Persons);

        return sortedList.ToArray();
    }
}

Et vous pouvez créer un EntitiySorter comme ceci:

IEntitySorter<Person> sorter = EntitySorter<Person>
    .OrderBy(p => p.Name)
    .ThenByDescending(p => p.Id);

Ou comme ça:

var sorter = EntitySorter<Person>
     .OrderByDescending("Address.City")
     .ThenBy("Id");
6
Steven

Je l'ai fait:

using System.Linq.Expressions;

namespace System.Linq
{
    public static class LinqExtensions
    {

        public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, string field, string dir = "asc")
        {
            // parametro => expressão
            var parametro = Expression.Parameter(typeof(TSource), "r");
            var expressao = Expression.Property(parametro, field);
            var lambda = Expression.Lambda(expressao, parametro); // r => r.AlgumaCoisa
            var tipo = typeof(TSource).GetProperty(field).PropertyType;

            var nome = "OrderBy";
            if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase))
            {
                nome = "OrderByDescending";
            }
            var metodo = typeof(Queryable).GetMethods().First(m => m.Name == nome && m.GetParameters().Length == 2);
            var metodoGenerico = metodo.MakeGenericMethod(new[] { typeof(TSource), tipo });
            return metodoGenerico.Invoke(source, new object[] { source, lambda }) as IOrderedQueryable<TSource>;

        }

        public static IOrderedQueryable<TSource> ThenBy<TSource>(this IOrderedQueryable<TSource> source, string field, string dir = "asc")
        {
            var parametro = Expression.Parameter(typeof(TSource), "r");
            var expressao = Expression.Property(parametro, field);
            var lambda = Expression.Lambda<Func<TSource, string>>(expressao, parametro); // r => r.AlgumaCoisa
            var tipo = typeof(TSource).GetProperty(field).PropertyType;

            var nome = "ThenBy";
            if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase))
            {
                nome = "ThenByDescending";
            }

            var metodo = typeof(Queryable).GetMethods().First(m => m.Name == nome && m.GetParameters().Length == 2);
            var metodoGenerico = metodo.MakeGenericMethod(new[] { typeof(TSource), tipo });
            return metodoGenerico.Invoke(source, new object[] { source, lambda }) as IOrderedQueryable<TSource>;
        }

    }
}

Utilisation :

example.OrderBy("Nome", "desc").ThenBy("other")

Fonctionne comme:

example.OrderByDescending(r => r.Nome).ThenBy(r => r.other)
6

Vous devez utiliser la bibliothèque de requêtes LINQ Dynamic afin de passer les paramètres au moment de l'exécution,

Cela permettra aux instructions linq comme

string orderedBy = "Description";
var query = (from p in products
            orderby(orderedBy)
            select p);
4
Nicholas Murray