2009-11-04 2 views
9

Załóżmy, że mam dwie klasy:dostępu zagnieżdżone właściwości z dynamicznym lambda wykorzystujące Linq.Expression

class person 
{ 
    int ID 
    string name 
    Address address 
} 
class address 
{ 
    int ID 
    string street 
    string country 
} 

Klasy te są bardziej lub mniej podane, są odwzorowywane przez nHibernate szczerze mówiąc :)

W siatce (datatables.net jako podstawa) chciałbym mieć sortowanie niezależne od typu.

Dlatego stworzyłem wyrażenie lambda:

var param = Expression.Parameter(typeof(T), typeof(T).Name); 
    var sortExpression = Expression.Lambda<Func<T, object>> 
           (Expression.Convert(Expression.Property(param, "Property to sort"), typeof(object)), param); 

Gdybym przekazać osobie jako typ T i zastąpić „obiekt do sortowania” z „nazwą” działa dobrze (tworzy poprawne lambda). Jeśli właściwość sortowania jest „address.street” to nie zadziała, rzucać mi następujący błąd:

Property 'address.street' is not defined for type 'person' 

Widzę tylko jedno rozwiązanie do tej pory, ale nie na tyle jasne ... Chciałbym spróbować podzielić ciąg zawierający nazwę właściwości (podzieloną przez)

Czy ktoś może dać lepsze rozwiązanie? Muszę dodać sortExpression do obiektu IQueryable query.OrderBy(sortExpression).

Nie jestem pewien, czy mój tytuł jest jasny, proszę go poprawić.

Z góry dziękuję.

Odpowiedz

2

Wygląda na to, że próbujesz przepisać Microsoft DynamicQuery. Dlaczego zamiast tego po prostu użyć tego?

Oto przykład:

IQueryable<Foo> myQuery = GetHibernateQuery(); 
myQuery = myQuery.OrderBy("address.street"); 
+0

Ponieważ otrzymuję tylko obiekt IQueryable, który zawiera już garść danych, dodam stronicowanie i tak dalej, następnie ładuję tylko dane pozostałe w zapytaniu. Odbywa się to za pomocą nHibernate. Tylko dla sortowania nie może to być rozwiązanie (mam nadzieję). – griti

+0

To jest moja uwaga. DynamicQuery może już to zrobić "dodaj sortowanie do istniejącego zapytania". Mam na moim blogu rozwiązanie demonstracyjne, które właśnie to robi. http://blogs.teamb.com/craigstuntz/2009/04/27/38243/ –

+0

o dzięki, przeczytam przez to – griti

12

Co nie jest jasne?

Trzeba je rozdzielić, a następnie użyć:

Expression.Property(Expression.Property(param, "address"), "street") 
+0

uhm i jak by to zrobić, jeśli może pojawi się coś w stylu "osoba.firma.adres.street"? Po prostu nie wiem, jak "zagnieździć" to .. lub jeśli istnieją inne sposoby, takie jak Craig opublikowane ... – griti

+1

Expression.Property (Expression.Property (Expression.Property (Expression.Property (param, "osoba")), "firma"), "adres"), "ulica") Prosta pętla zadziała. – LukLed

+0

nie ręcznie? :) – griti

4

Oto bardziej ogólna wersja odpowiedzi LukLed za:

protected MemberExpression NestedExpressionProperty(Expression expression, string propertyName) 
    { 
     string[] parts = propertyName.Split('.'); 
     int partsL = parts.Length; 

     return (partsL > 1) 
      ? 
      Expression.Property( 
       NestedExpressionProperty(
        expression, 
        parts.Take(partsL - 1) 
         .Aggregate((a, i) => a + "." + i) 
       ), 
       parts[partsL - 1]) 
      : 
      Expression.Property(expression, propertyName); 
    } 

Można go używać tak:

var paramExpression = Expression.Parameter(this.type, "val"); 
var firstProp = NestedExpressionProperty(paramExpression,"address.street"); 
+0

Jest to doskonała i wielokrotnego użytku metoda rozwiązania tego problemu. Dziękuję Ci. –

+0

Doskonała odpowiedź +1. Możesz to zrobić w jednej linijce: 'return propertyName.Split ('.') Aggregate (expression, (body, member) => Expression.PropertyOrField (body, member));'. – nawfal

+0

Jeśli nie lubisz fantazyjnych linijek, być może bardziej czytelna wersja używa 'foreach', np .:' Wyrażenie body = wyrażenie; foreach (element var w propertyName.Split ('.')) { body = Expression.PropertyOrField (body, member); } ciało powrotu; ' – nawfal

0

Wypróbuj ten jeden

public static IQueryable<T> SortIQueryable<T>(IQueryable<T> data, string fieldName, string sortOrder) 
    { 
     if (string.IsNullOrWhiteSpace(fieldName)) return data; 
     if (string.IsNullOrWhiteSpace(sortOrder)) return data; 

     var param = Expression.Parameter(typeof(T), "i"); 

     MemberExpression property = null; 
     string[] fieldNames = fieldName.Split('.'); 
     foreach (string filed in fieldNames) 
     { 
      if (property == null) 
      { 
       property = Expression.Property(param, filed); 
      } 
      else 
      { 
       property = Expression.Property(property, filed); 
      } 
     } 

     Expression conversion = Expression.Convert(property, typeof(object));//Expression.Property(param, fieldName) 
     var mySortExpression = Expression.Lambda<Func<T, object>>(conversion, param); 

     return (sortOrder == "desc") ? data.OrderByDescending(mySortExpression) 
      : data.OrderBy(mySortExpression); 
    }