2012-07-13 15 views
10

Estoy intentando generar una cláusula LINQ OrderBy usando expresiones lambda con una entrada del nombre de columna de una entidad como cadena (en la variable "sortOn" a continuación).Creando una expresión OrderBy Lambda basada en la propiedad de la entidad hijo

El código siguiente funciona bien para un valor sortOn como "Código" generación de la lambda

p => p.Code 

pero también me gustaría ordenar en una entidad secundaria, donde el lambda podría ser

p => p.Category.Description 

Por lo tanto, en este caso, me gustaría establecer sortOn = "Category.Description" y generar la expresión lamdba correcta.

¿Esto es posible? Cualquier sugerencia sobre la mejor manera de hacer esto sería bienvenida.

Este código funciona bien para el caso sencillo:

var param = Expression.Parameter(typeof (Product), "p"); 

var sortExpression = Expression.Lambda<Func<Product, object>>(
    Expression.Property(param, sortOn), param); 

if (sortAscending ?? true) 
{ 
    products = products.OrderBy(sortExpression); 
} 
else 
{ 
    products = products.OrderByDescending(sortExpression); 
} 

El caso de uso para este problema está mostrando una cuadrícula de datos y ser capaz de ordenar los datos, simplemente pasando el nombre de la columna que desee ordenar de vuelta al servidor. Me gustaría hacer que la solución sea genérica, pero he comenzado a usar un tipo particular (Producto en el ejemplo) por ahora.

+0

¿Por qué estás creando expresiones manualmente? –

Respuesta

18

Esto generará expresión lambda apropiada:

var sortOn = "Category.Description"; 
var param = Expression.Parameter(typeof(Product), "p"); 
var parts = sortOn.Split('.'); 

Expression parent = param; 

foreach (var part in parts) 
{ 
    parent = Expression.Property(parent, part); 
} 

var sortExpression = Expression.Lambda<Func<Product, object>>(parent, param); 
0

Si no es necesario expresiones, ¿qué tal:

products = products.Orderby(p1 => p1.Code).ThenBy(p2 => p2.Category.Description) 
2

Puede utilizar el Dynamic LINQ Query Library hacer esto fácilmente. Suponiendo que haya un IQueryable<T> implementation de Product, puede hacerlo fácilmente:

IQueryable<Product> products = ...; 

// Order by dynamically. 
products = products.OrderBy("Category.Description"); 

La entrada del blog tiene una link to the libary, y usted tiene que construir/incluir el proyecto en su solución a sí mismo, pero funciona muy bien, y el análisis es muy robusto. Le impide tener que escribir el código de análisis usted mismo; incluso para algo tan simple, si los requisitos se amplían, la biblioteca lo tiene cubierto, mientras que una solución de cosecha propia no lo hace.

También tiene una serie de otros operadores dinámicos (Select, Where, etc.) para que pueda realizar otras operaciones dinámicas.

No hay magia debajo del capó, solo analiza las cadenas que lo pasa y luego crea las expresiones lambda basadas en los resultados del análisis sintáctico.

+0

Gracias por la sugerencia. Podría haber marcado como una respuesta, pero Tomek me dio el enfoque que quería. Definitivamente considerará esto como una solución alternativa. – Appetere

0

Hola también puede crear un método de extensión similar que puede ordenar a cualquier profundidad no sólo acaba de niño

 public static IEnumerable<TSource> CustomOrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) 
    { 
     List<string> list=new List<string>(); 
     List<TSource> returnList=new List<TSource>(); 
     List<int> indexList = new List<int>(); 

     if (source == null) 
      return null; 
     if (source.Count() <= 0) 
      return source; 
     source.ToList().ForEach(sc=>list.Add(keySelector(sc).ToString())); //Extract the strings of property to be ordered 

     list.Sort(); //sort the list of strings 

     foreach (string l in list) // extract the list of indexes of source according to the order 
     { 
      int i=0; 
      //list.ForEach(l => 

       foreach (var s in source.ToList()) 
       { 
        if (keySelector(s).ToString() == l) 
         break; 
        i++; 
       } 
       indexList.Add(i); 
     } 
     indexList.ForEach(i=>returnList.Add(source.ElementAt(i))); //rearrange the source according to the above extracted indexes 
     return returnList; 
    } 
} 
public class Name 
{ 
    public string FName { get; set; } 
    public string LName { get; set; } 
} 
public class Category 
{ 
    public Name Name { get; set; } 
} 
public class SortChild 
{ 
    public void SortOn() 
    { 
     List<Category> category = new List<Category>{new Category(){Name=new Name(){FName="sahil",LName="chauhan"}}, 
      new Category(){Name=new Name(){FName="pankaj",LName="chauhan"}}, 
      new Category(){Name=new Name(){FName="harish",LName="thakur"}}, 
      new Category(){Name=new Name(){FName="deepak",LName="bakseth"}}, 
      new Category(){Name=new Name(){FName="manish",LName="dhamaka"}}, 
      new Category(){Name=new Name(){FName="arev",LName="raghaka"}} 
     }; 
     var a = category.CustomOrderBy(s => s.Name.FName); 

    } 

} 

Su método personalizado y en este momento funciona sólo para propiedad de cadena única sin embargo, puede ser reactified utilizando los genéricos para trabajar para cualquier tipo primitivo. Espero que esto sea de ayuda.

0

Aquí hay una extensión Método de PedidoPor que funciona para cualquier cantidad de parámetros anidados.

public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string key, bool asc = true) 
{ 
    try 
    { 
    string orderMethodName = asc ? "OrderBy" : "OrderByDescending"; 
    Type type = typeof(T); 
    Type propertyType = type.GetProperty(key)?.PropertyType; ; 

    var param = Expression.Parameter(type, "x"); 
    Expression parent = param; 

    var keyParts = key.Split('.'); 
    for (int i = 0; i < keyParts.Length; i++) 
    { 
     var keyPart = keyParts[i]; 
     parent = Expression.Property(parent, keyPart); 

     if (keyParts.Length > 1) 
     { 
     if (i == 0) 
     { 
      propertyType = type.GetProperty(keyPart).PropertyType; 
     } 
     else 
     { 
      propertyType = propertyType.GetProperty(keyPart).PropertyType; 
     } 
     } 
    } 

    MethodCallExpression orderByExpression = Expression.Call(
     typeof(Queryable), 
     orderMethodName, 
     new Type[] { type, propertyType }, 
     query.Expression, 
     CreateExpression(type, key) 
    ); 

    return query.Provider.CreateQuery<T>(orderByExpression); 
    } 
    catch (Exception e) 
    { 
    return query; 
    } 
} 

El método CreateExpression que se utiliza en mi solución se define in this post.

El uso del método de extensión OrderBy es el siguiente.

IQueryable<Foo> q = [Your database context].Foos.AsQueryable(); 
IQueryable<Foo> p = null; 

p = q.OrderBy("myBar.name"); // Ascending sort 
// Or 
p = q.OrderBy("myBar.name", false); // Descending sort 
// Materialize 
var result = p.ToList(); 

El tipo Foo y sus propiedades también se han tomado de la misma puesto como método CreateExpression.

Espero que el mensaje le sea útil.

Cuestiones relacionadas