2010-04-28 8 views
41

Quiero hacer esto:¿Cómo puedo hacer un OrderBy con un parámetro de cadena dinámico?

var orderBy = "Nome, Cognome desc"; 

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

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

¿Hay una sobrecarga OrderBy disponibles que acepta un parámetro de cadena?

+1

Posible duplicado de [¿Cómo especifico dinámicamente el argumento Linq OrderBy?] (Http://stackoverflow.com/questions/7265186/how-do-i-specify-the-linq-orderby-argument-dynamically) – Korayem

+0

Aquí es una buena respuesta. Sin ninguna tercera biblioteca. https://stackoverflow.com/a/233505/714828 –

Respuesta

37

Absolutamente. Puede usar la Biblioteca de consultas dinámicas LINQ, que se encuentra on Scott Guthrie's blog. También hay una versión actualizada disponible on CodePlex.

Le permite crear OrderBy cláusulas, Where cláusulas, y casi todo lo demás al pasar en parámetros de cadena. Funciona muy bien para la creación de código genérico para la clasificación/filtrado de rejillas, etc.

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

var query = DbContext.Data 
    .Where(/* ... */) 
    .Select(/* ... */) 
    .OrderBy("Foo ascending"); 
+2

¿Hay algún problema de seguridad con esto? Al igual que en la inyección de LINQ, ¿no sería peligroso permitir la entrada del usuario en el campo 'OrderBy'? ej. '.OrderBy (" UserInput ascendente ").' – jthomperoo

+0

Esto está disponible como un paquete Nuget para los interesados ​​ – ElliotSchmelliot

5

Es necesario utilizar la biblioteca de consultas LINQ Dynamic con el fin de pasar parámetros en tiempo de ejecución,

Esto permitirá que las declaraciones de LINQ como

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

Mira en este blog here. Describe una forma de hacerlo, definiendo un EntitySorter<T>.

Se permite pasar en una IEntitySorter<T> en sus métodos de servicio y utilizar de esta manera:

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

     return sortedList.ToArray(); 
    } 
} 

Además, puede crear un EntitiySorter así:

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

O así:

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

Gran respuesta: encapsulando la clasificación en su propia clase es una solución flexible y reutilizable – AlexFoxGill

55

Si está utilizando LINQ-to-objects y no quiere Para tomar una dependencia de una biblioteca externa, no es difícil lograr lo que desea.

La cláusula OrderBy() acepta un Func<TSource, TKey> que obtiene una clave de clasificación de un elemento de origen. Se puede definir la función fuera de la OrderBy() cláusula:

Func<Item, Object> orderByFunc = null; 

A continuación, puede asignar a diferentes valores en función de los criterios de clasificación:

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

A continuación, se puede ordenar:

var sortedItems = items.OrderBy(orderByFunc); 

Este ejemplo asume que el tipo de fuente es Item que tiene las propiedades Name y Rank.

Tenga en cuenta que en este ejemplo TKey es Object para no restringir los tipos de propiedades que se pueden ordenar. Si el func devuelve un tipo de valor (como Int32), se colocará en un recuadro al ordenar y eso es algo ineficaz. Si puede restringir TKey a un tipo de valor específico, puede evitar este problema.

+3

Limpio, excelente respuesta, el TKey fue lo que me estaba confundiendo y asumí que estaba definido más arriba en la cadena y no podía ser estampado tan fácilmente. He estado usando la resolución DynLinq por un tiempo. Gracias por esto. –

+0

Si necesito que el primer orden dinámico sea descendente, y el segundo para ascender, ¿hay algún truco para eso? –

+2

@YuvalA .: Si una de las propiedades es numérica (esto incluye 'DateTime.Ticks') puede negar el valor para ordenar en el orden opuesto. De lo contrario, puede usar 'OrderBy' o' OrderByDescending' condicionalmente: 'var sortedItems = reverse? items.OrderByDescending (orderByFunc): items.OrderBy (orderByFunc) '. –

22

Otra solución 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)); 
+3

Esto no funciona en Linq to Entity Framework ya que arroja este error 'LINQ to Entities no reconoce el método 'System.Object GetValue (System.Object, System.Object [])' método, y este método no se puede traducir a almacenar expresión. – Korayem

+0

con ese error, podría intentar: var orderByAddress = items.AsEnumerable(). OrderBy (x => propertyInfo.GetValue (x, null)) –

+2

No es bueno si quiere paginar los resultados. Dicha paginación vendría después del OrderBy con Skip() y Take() ... –

5

El más simple & la mejor solución:

mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s)); 
+0

¿estás seguro de que esto no tiene error de sintaxis? –

+0

agregar utilizando System.Linq; –

+0

Esto probablemente no funcionará con 'IQueryable'. –

-1

En una respuesta anterior:

El más simple & la mejor solución:

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

Hay un error de sintaxis, ,null hay que añadir:

mylist.OrderBy(s => s.GetType().GetProperty("PropertyName").GetValue(s,null)); 
+1

Esto debería ser un comentario en @Kasper Roma. – Eiko

5

usted no necesita una biblioteca externa para esto. El siguiente código funciona para LINQ a SQL/entidades.

/// <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 copiado de https://stackoverflow.com/a/16208620/111438)

0

así lo hice:

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<Func<TSource, string>>(expressao, parametro); // r => r.AlgumaCoisa 
      if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase)){ 
       return source.OrderByDescending(lambda); 
      } 
      return source.OrderBy(lambda); 
     } 

     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 
      if (string.Equals(dir, "desc", StringComparison.InvariantCultureIgnoreCase)) 
      { 
       return source.ThenByDescending(lambda); 
      } 
      return source.ThenBy(lambda); 
     } 

    } 
} 

Uso:

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

trabajo como:

example.OrderByDescending(r => r.Nome).ThenBy(r => r.other) 
Cuestiones relacionadas