2009-03-03 13 views
6

Come faccio ad ordinare da un valore di stringa passato sulla mia lista di oggetti? Ho bisogno di fare il paging e l'ordinamento sui miei oggetti List (Of), il paging non è un problema, ma non so chi possa far funzionare l'ordine.linq Ordina per una lista (di myObjects)

Qui è quello che sto facendo attualmente e che sta funzionando grande:

Return returnReports.Skip(PageSize * (PageNumber-1)).Take(PageSize).ToList() 

Come faccio ad avere questo lavoro?

Return returnReports.OrderBy(SortColumn).Skip(skip).Take(PageSize).ToList() 

SortColumn essere un valore stringa passata

risposta

11

VB

Module OrderByExtensions 
    <System.Runtime.CompilerServices.Extension()> _ 
    Public Function OrderByPropertyName(Of T)(ByVal e As IEnumerable(Of T), ByVal propertyName As String) As IOrderedEnumerable(Of T) 
    Dim itemType = GetType(T) 
    Dim prop = itemType.GetProperty(propertyName) 
    If prop Is Nothing Then Throw New ArgumentException("Object does not have the specified property") 
    Dim propType = prop.PropertyType 
    Dim funcType = GetType(Func(Of ,)).MakeGenericType(itemType, propType) 
    Dim parameter = Expression.Parameter(itemType, "item") 
    Dim exp = Expression.Lambda(funcType, Expression.MakeMemberAccess(parameter, prop), parameter) 
    Dim params = New Object() {e, exp.Compile()} 
    Return DirectCast(GetType(OrderByExtensions).GetMethod("InvokeOrderBy", Reflection.BindingFlags.Static Or Reflection.BindingFlags.NonPublic).MakeGenericMethod(itemType, propType).Invoke(Nothing, params), IOrderedEnumerable(Of T)) 
    End Function 
    Private Function InvokeOrderBy(Of T, U)(ByVal e As IEnumerable(Of T), ByVal f As Func(Of T, U)) As IOrderedEnumerable(Of T) 
    Return Enumerable.OrderBy(e, f) 
    End Function 
End Module 

C#

public static class OrderByExtensions 
{ 
    public static IOrderedEnumerable<T> OrderByPropertyName<T>(this IEnumerable<T> e, string name) 
    { 
    var itemType = typeof(T); 
    var prop = itemType.GetProperty(name); 
    if (prop == null) throw new ArgumentException("Object does not have the specified property"); 
    var propType = prop.PropertyType; 
    var funcType = typeof(Func<,>).MakeGenericType(itemType, propType); 
    var parameter = Expression.Parameter(itemType, "item"); 
    var memberAccess = Expression.MakeMemberAccess(parameter, prop); 
    var expression = Expression.Lambda(funcType, memberAccess, parameter); 
    var x = typeof(OrderByExtensions).GetMethod("InvokeOrderBy", BindingFlags.Static | BindingFlags.NonPublic); 
    return (IOrderedEnumerable<T>)x.MakeGenericMethod(itemType, propType).Invoke(null, new object[] { e, expression.Compile() }); 
    } 
    static IOrderedEnumerable<T> InvokeOrderBy<T, U>(IEnumerable<T> e, Func<T, U> f) 
    { 
    return e.OrderBy(f); 
    } 
} 
+0

Non funzionerà quando SortColumn viene passato come una stringa. –

+0

Oh, non ho notato che nella domanda, la modifica ... –

+0

Hai perso questo dal parametro e nel codice C#, in quanto non sono metodi di estensione. – Richard

3

Far passare la colonna di ordinamento come una funzione.

Quindi sarebbe

public SomeList Foo(Function<Foo, bool> sortFunction, int skip, int PageSize) 
{ 
    return returnReports.OrderBy(sortFunction).Skip(skip).Take(PageSize).ToList(); 
} 

chiamare in questo modo

SomeList(f => f.Bar, 5, 10); 
+0

azione delegato non ritorna un valore. –

+0

Hai ragione, correggerò. –

1

Non puoi farlo (facilmente) se hai appena passato una stringa. Potresti avere una mappa da String a Func<IEnumerable<Report>, IEnumerable<Report>>, ad es. (In C#)

// Horrible type. Ick. 
private static readonly 
    Dictionary<string, Func<IEnumerable<Report>,IEnumerable<Report>>> 
    Orderings = 
    new Dictionary<string, Func<IEnumerable<Report>,IEnumerable<Report>>> 
{ 
    { "FirstColumn", (IEnumerable<Report> reports) => 
          reports.OrderBy(report => report.FirstColumn) }, 
    { "SecondColumn", (IEnumerable<Report> reports) => 
          reports.OrderBy(report => report.SecondColumn) }, 

    (etc) 
}; 

Quindi utilizzare:

// For production usage, include some error checking! 
return Orderings[sortColumn].Skip(skip).Take(pageSize).ToList(); 

Se si può ottenere SortColumn da passare come un adeguato Func (possibilmente rendendo il vostro metodo generico) che eviterebbe la confusione qui.

2

se si lavora con un database come fonte di dati, è possibile utilizzare il progetto Dynamic LINQ che consente di specificare i parametri della clausola Where come stringa.

Se si sta lavorando con "Linq agli oggetti" di quanto necessario sarà necessario creare la funzione lambda che viene passata come argomento dinamicamente. È possibile farlo utilizzando i metodi "Expression.Xyz" per costruire l'albero delle espressioni e quindi utilizzando il metodo "Compile" che trasforma l'albero delle espressioni in un delegato richiamabile (di tipo Func <>) che è possibile utilizzare come argomento per Dove. Un esempio su come costruire l'albero delle espressioni può essere trovato in another SO thread here.

0

Invece di. OrdineBy, è possibile utilizzare .Sort. Basta creare una classe Comparer per il tuo oggetto come this.

public class FooComparer : IComparer<Foo> 
    { 
     private readonly string _sortBy; 
     private readonly bool _sortDesc; 

     public FooComparer(string sortBy) 
     { 
     _sortBy = sortBy.ToLower(); 
     _sortDesc = _sortBy.EndsWith(" desc"); 
     _sortBy = _sortBy.Replace(" asc", string.Empty).Replace(" desc", string.Empty); 
     } 

     //implement IComparer method 
     public int Compare(Foo x, Foo y) 
     { 
     int ret = 0; 

     switch (_sortBy) 
     { 
      //must match lowercase sortname 
      case "date": 
       ret = DateTime.Compare(x.SomeDate, y.SomeDate); 
       break; 
      //other properties you can sort by as above... 

     } 

     //if there is a tie, break it consistently - this will be specific to your object 
     if (ret == 0) 
     { 
      ret = (DateTime.Compare(x.InsertDate, y.InsertDate)); 
     } 

     if (_sortDesc) 
     { 
      ret *= -1; 
     } 

     return ret; 
     } 
    } 

Poi la chiamata sorta diventa:

Return Foo.Sort(new FooComparer(sortName + " " + sortOrder) 

così, il vostro sarebbe diventato (default ordinamento per ascendente quando non è specificato):

returnReports.Sort(new ReturnReportsComparer(SortColumn)); 

return returnReports.Skip(skip).Take(PageSize).ToList()