2012-07-05 10 views
10

Utilizzando Entity Framework 4, sto cercando di implementare l'ordinamento dinamico basato su una raccolta di nomi di membri. Fondamentalmente, l'utente può selezionare i campi da ordinare e l'ordine dell'ordinamento. Ho esaminato esempi di espressioni tree e non riesco a metterlo insieme. Ecco alcuni dettagli:Come si crea un albero di espressioni per l'ordinamento in fase di esecuzione?

Raccolta dei nomi delle colonne:

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"); 

La classe video viene definito come segue:

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; 

Quello che vorrei fare è enumerare attraverso la raccolta sortColumns di costruire un albero di espressioni in fase di esecuzione. Inoltre, l'utente può specificare l'ordinamento crescente o decrescente e l'albero delle espressioni deve gestire entrambi.

ho provato la libreria dinamica LINQ per VS 2008, ma non sembra lavorare in VS 2010. (potrei fare qualcosa di sbagliato.)

La linea di fondo è che ho bisogno di un albero di espressione in modo dinamico ordina la raccolta di video in base all'input dell'utente. Qualsiasi aiuto sarebbe apprezzato.

risposta

14

Per prima cosa è necessario il metodo di estensione OrderBy che @Slace ha scritto here. Tutto il credito a Slace per un fantastico pezzo di codice e di gran lunga la parte più difficile della soluzione! Ho apportato una leggera modifica perché funzioni con la situazione specifica:

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); 
    } 
} 

Creare un metodo per ordinare l'elenco. Un paio di cose da notare nel metodo seguito:

  1. Il List<string> viene convertito in un IQueryable<string> dal Enumerable operatori non prendere alberi di espressione.
  2. Il metodo scorre l'elenco delle colonne di ordinamento in ordine inverso (supponendo che si vuole dare il primo elemento della lista la più alta priorità di ordinamento)

.

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); 
    } 
} 

Si dovrebbe quindi essere in grado di utilizzare il metodo come questo:

// 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

È possibile utilizzare il ListSortDirection già definito (http://msdn.microsoft.com/en-us/library/system .componentmodel.listsortdirection.aspx) enum;) –

+0

@BennorMcCarthy Fantastico! Grazie. Aggiornerò la risposta –

+0

'@' Kevin - Esattamente quello di cui avevo bisogno, grazie! – James

0

è esattamente quello che mi serviva Kevin. Quello che ho notato è che se usi l'orderby prende solo l'ultima selezione di orderby.

ho aggiunto questo metodo (ThenBy) al mio e sembra funzionare bene

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); 
    } 
Problemi correlati