2013-07-15 7 views
10

Sto utilizzando questa funzione di ordine dinamico linq che ho ottenuto da here.Ordine linq dinamico in base alla proprietà annidata con proprietà null

Questo funziona bene con le proprietà nidificate così ho potuto fare questo:

var result = data.OrderBy("SomeProperty.NestedProperty"); 

Il problema è che se SomeProperty è nullo quindi eseguire l'OrderBy sul NestedProperty tiri il famoso "Oggetto non impostato a un'istanza di un oggetto".

La mia ipotesi è che ho bisogno di personalizzare le seguenti linee per gestire l'eccezione:

expr = Expression.Property(expr, pi); 

// Or 

LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);  

ho pensato di creare un corpo di dichiarazione in cui ho potuto nel peggiore dei casi utilizzare un tentativo di cattura, ma che didn' funziona come non si può avere corpi di istruzioni all'interno delle istruzioni di linq: "Un'espressione lambda con un corpo di istruzioni non può essere convertita in un albero di espressione"

Mi sono persa qui, qualche suggerimento su come posso realizzare questo ?

A proposito, questo è per Linq to Objects, non relativo al database.

+0

Credo che questa linea 'expr = Expression.Property (espressione, pi);' 'set expr' a null e ulteriore codice non gestirlo. Il modo più semplice per risolverlo è 'expr = Expression.Property (expr, pi) ?? default (T); '. Tuttavia, in questo caso dovrai verificare se stai bene con l'ordine applicato. – Tommi

+0

È un buon punto, in realtà ciò renderebbe errato l'ordinamento, idealmente i valori nulli dovrebbero essere "raggruppati" insieme. –

+0

vedere se questo ti aiuta in qualsiasi modo http://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet?lq=1 – Ehsan

risposta

5
static void Main(string[] args) 
{ 
    var data = new List<MyType>() { 
     new MyType() { SomeProperty = new Inner() { NestedProperty = "2" }}, 
     new MyType() { SomeProperty = new Inner() { NestedProperty = "1" }}, 
     new MyType() { SomeProperty = new Inner() { NestedProperty = "3" }}, 
     new MyType(), 
    }.AsQueryable(); 
    var sorted = data.OrderBy(x => GetPropertyValue(x, "SomeProperty.NestedProperty")); 

    foreach (var myType in sorted) 
    { 
     try 
     { 
      Console.WriteLine(myType.SomeProperty.NestedProperty); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Null"); 
     } 
    } 
} 

public static object GetPropertyValue(object obj, string propertyName) 
{ 
    try 
    { 
     foreach (var prop in propertyName.Split('.').Select(s => obj.GetType().GetProperty(s))) 
     { 
      obj = prop.GetValue(obj, null); 
     } 
     return obj; 
    } 
    catch (NullReferenceException) 
    { 
     return null; 
    } 
} 
+0

Eccezionale, questo sembra più semplice della risposta di Marc Gravel, il che mi fa meravigliare. Tuttavia, tutti i miei test sembrano funzionare bene e tu mi hai reso un uomo felice. –

+0

Molto intelligente. Bel trucco con la ricorsione. Microsoft dovrebbe fornire questo come metodo di estensione nei framework futuri. – arviman

+0

Cosa succede se invece di SomeProperty hai una lista ? Come dovrebbe essere modificato il codice di cui sopra per gestire il caso? – demonicdaron

3

ne dite di farmaci generici:

Helper Metodo:

public static Expression<Func<TEntity, TResult>> GetExpression<TEntity, TResult>(string prop) 
     { 
      var param = Expression.Parameter(typeof(TEntity), "p"); 
      var parts = prop.Split('.'); 

      Expression parent = parts.Aggregate<string, Expression>(param, Expression.Property); 
      Expression conversion = Expression.Convert(parent, typeof (object)); 

      var tryExpression = Expression.TryCatch(Expression.Block(typeof(object), conversion), 
                Expression.Catch(typeof(object), Expression.Constant(null))); 

      return Expression.Lambda<Func<TEntity, TResult>>(tryExpression, param); 
     } 

Esempio Gerarchia:

public class A 
    { 
     public A(B b) 
     { 
      B = b; 
     } 

     public B B { get; set; } 
    } 

    public class B 
    { 
     public B(C c) 
     { 
      C = c; 
     } 

     public C C { get; set; } 
    } 

    public class C 
    { 
     public C(int id) 
     { 
      this.Id = id; 
     } 

     public int Id { get; set; } 
    } 

Esempio:

var list = new List<B> 
      { 
       new B(new A(new C(1))), 
       new B(new A(new C(2))), 
       new B(new A(new C(3))), 
       new B(new A(null)), 
       new B(null) 
      }.AsQueryable(); 

var ordered = list.OrderByDescending(GetExpression<B, Object>("AProp.CProp.Id")); 

uscita:

3 
2 
1 
Null 
Null 
+1

Sei un genio, grazie mille! – Alexander

Problemi correlati