2012-07-05 17 views
10

Al usar Entity Framework 4, intento implementar la ordenación dinámica en función de una colección de nombres de miembros. Básicamente, el usuario puede seleccionar los campos para ordenar y el orden de la clasificación. He visto ejemplos de árbol de expresiones y no puedo unir esto. He aquí algunos detalles:¿Cómo creo un árbol de expresiones para la ordenación en tiempo de ejecución?

colección de nombres de columna:

public List<string> sortColumns; 
sortColumns = new List<string>(); 

/// Example subset of video fields. The collection will vary. 
sortColumns.Add("Width"); 
sortColumns.Add("Height"); 
sortColumns.Add("Duration"); 
sortColumns.Add("Title"); 

está definida la clase de vídeo de la siguiente manera:

public class Video 
{ 
    public string Title { get; set; } 
    public int Width { get; set; } 
    public int Height { get; set; } 
    public float Duration { get; set; } 
    public string Filename { get; set; } 
    public DateTime DateCreated { get; set; } 
    . 
    . 
    . 
} 
public List<Video> Videos; 

Lo que me gustaría hacer es enumerar a través de la colección sortColumns para construir un árbol de expresiones en tiempo de ejecución. Además, el usuario podría especificar una ordenación ascendente o descendente y el árbol de expresiones también debería manejarlo.

Probé la biblioteca dinámica LINQ para VS 2008, pero no parece funcionar en VS 2010. (que podría estar haciendo algo mal.)

La conclusión es que necesito un árbol de expresión de forma dinámica ordenar la colección de videos en función de la entrada del usuario. Cualquier ayuda sería apreciada.

Respuesta

14

Primero necesita el método de extensión OrderBy que @Slace escribió here. ¡Todo crédito a Slace por una increíble pieza de código y, con mucho, la parte más difícil de la solución! Hice una pequeña modificación para que funcione con su situación específica:

public static class QueryableExtensions 
{ 
    public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortProperty, ListSortDirection sortOrder) 
    { 
     var type = typeof(T); 
     var property = type.GetProperty(sortProperty); 
     var parameter = Expression.Parameter(type, "p"); 
     var propertyAccess = Expression.MakeMemberAccess(parameter, property); 
     var orderByExp = Expression.Lambda(propertyAccess, parameter); 
     var typeArguments = new Type[] { type, property.PropertyType }; 
     var methodName = sortOrder == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending"; 
     var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp)); 

     return source.Provider.CreateQuery<T>(resultExp); 
    } 
} 

Cree un método para ordenar la lista. Un par de cosas a tener en cuenta en el siguiente método:

  1. El List<string> se convierte en un IQueryable<string> desde Enumerable operadores no tienen árboles de expresión.
  2. se repite el método a través de la lista de columnas de orden en orden inverso (asumiendo que usted quiere dar el primer elemento de la lista de la prioridad más alta clase)

.

private void PrintVideoList(IEnumerable<string> sortColumns, ListSortDirection sortOrder) 
{ 
    var videos = this.GetVideos(); 
    var sortedVideos = videos.AsQueryable(); 

    foreach (var sortColumn in sortColumns.Reverse()) 
    { 
     sortedVideos = sortedVideos.OrderBy(sortColumn, sortOrder); 
    } 

    // Test the results 
    foreach (var video in sortedVideos) 
    { 
     Console.WriteLine(video.Title); 
    } 
} 

A continuación, debería ser capaz de utilizar el método de la siguiente manera:

// These values are entered by the user 
var sortColumns = new List<string> { "Width", "Title", "Height" }; 
var sortOrder = ListSortDirection.Ascending; 

// Print the video list base on the user selection 
this.PrintVideoList(sortColumns, sortOrder); 
+0

Se podría utilizar el ListSortDirection ya definido (http://msdn.microsoft.com/en-us/library/system .componentmodel.listsortdirection.aspx) enum;) –

+0

@BennorMcCarthy Awesome! Gracias. Actualizaré la respuesta. –

+0

'@' Kevin - ¡Exactamente lo que necesitaba, gracias! – James

0

es exactamente lo que necesitaba Kevin. Lo que noté es que si usas el orden solo toma el último orden por selección.

que añade este método (ThenBy) a la mía y parece que funciona bien

public static IQueryable<T> ThenBy<T>(this IQueryable<T> source, string sortProperty, ListSortDirection sortOrder) 
    { 
     var type = typeof(T); 
     var property = type.GetTypeInfo().GetDeclaredProperty(sortProperty); 
     var parameter = Expression.Parameter(type, "p"); 
     var propertyAccess = Expression.MakeMemberAccess(parameter, property); 
     var orderByExp = Expression.Lambda(propertyAccess, parameter); 
     var typeArguments = new Type[] { type, property.PropertyType }; 
     var methodName = sortOrder == ListSortDirection.Ascending ? "ThenBy" : "ThenByDescending"; 
     var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp)); 

     return source.Provider.CreateQuery<T>(resultExp); 
    } 
Cuestiones relacionadas