2015-08-31 6 views
6

Ich habe ein Expression<Func<Entity, string>>, die entweder eine Eigenschaft oder eine verschachtelte EigenschaftenaccessorWie neu wickeln eine Linq Expression Baum

y => y.SearchColumn 

oder

y => y.SubItem.SubColumn 

ich einen Ausdruck sein können Baum dynamisch am Aufbau und möchte eine InvokeExpression wie diese bekommen

x => x.SearchColumn.Contains("foo"); 
x => x.Sub.SearchColumn.Contains("foo"); 

Ich bin mir ziemlich sicher, dass ich den Ausdruck auspacken kann ITZUNG Körper, dann gelten teilweise die x ParameterExpression es, aber ich kann nicht die magischen Beschwörungen herauszufinden, damit dies geschieht

So habe ich so etwas wie

Expression<Func<Entity, string>> createContains(Expression<Func<Entity, string>> accessor) { 
    var stringContains = typeof(String).GetMethod("Contains", new [] { typeof(String) }); 
    var pe = Expression.Parameter(typeof(T), "__x4326"); 
    return Expression.Lambda<Func<Entity, bool>>(
     Expression.Call(
      curryExpression(accessor.Body, pe), 
      stringContains, 
      Expression.Constant("foo") 
     ) 
     , pe 
    );    
} 

    static Expression curryExpression(Expression from, ParameterExpression parameter) { 
     // this doesn't handle the sub-property scenario 
     return Expression.Property(parameter, ((MemberExpression) from).Member.Name); 
     //I thought this would work but it does not 
     //return Expression.Lambda<Func<Entity,string>>(from, parameter).Body; 
    } 

Edit: Here is the full thing I'm trying to do along with the error

Antwort

3

Sie müssen zwei Dinge tun - erstens können Sie die gleichen accessor.Body verwenden, aber es wird auf falsche Parameter verweisen, wie Sie eine neue erstellt. Zweitens müssen Sie benutzerdefinierte ExpressionVisitor schreiben, die die gesamte Verwendung von Original yParameterExpression zu einem neuen erstellt ersetzen, so dass Ergebnis Ausdruck wird gut kompiliert werden.

Wenn Sie keine neuen ParameterExpression erstellen müssen, können Sie einfach denselben accessor.Body verwenden und die ursprüngliche ParameterExpression in eine neue Struktur übertragen.

So, hier ist meine Test-Arbeitskopie mit einer neuen ParameterExpression-https://dotnetfiddle.net/uuPVAl

und dem Code selbst

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     Expression<Func<Entity, string>> parent = (y) => y.SearchColumn; 
     Expression<Func<Entity, string>> sub = (y) => y.Sub.SearchColumn; 

     var result = Wrap(parent); 
     Console.WriteLine(result); 
     result.Compile(); 

     result = Wrap(sub); 
     Console.WriteLine(result); 
     result.Compile(); 

     result = Wrap<Entity>((y) => y.Sub.Sub.Sub.SearchColumn); 
     Console.WriteLine(result); 
     result.Compile(); 

    } 

    private static Expression<Func<T, bool>> Wrap<T>(Expression<Func<T, string>> accessor) 
    { 
     var stringContains = typeof (String).GetMethod("Contains", new[] {typeof (String)}); 
     var pe = Expression.Parameter(typeof (T), "__x4326"); 
     var newBody = new ParameterReplacer(pe).Visit(accessor.Body); 
     var call = Expression.Call(
      newBody, 
      stringContains, 
      Expression.Constant("foo") 
      ); 
     return Expression.Lambda<Func<T, bool>>(call, pe); 
    } 
} 

public class ParameterReplacer : ExpressionVisitor 
{ 
    private ParameterExpression _target; 

    public ParameterReplacer(ParameterExpression target) 
    { 
     _target = target; 
    } 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     // here we are replacing original to a new one 
     return _target; 
    } 
} 

public class Entity 
{ 
    public string SearchColumn { get; set; } 

    public Entity Sub { get; set; } 
} 

PS: Dieses Beispiel funktioniert nur, wenn Sie nur ein ParameterExpression in ursprünglicher Abfrage, sonst Besucher sollten sie unterscheiden

UPDATE

Hier ist meine Arbeits Antwort mit Ihrem vollständigen Beispiel in update - https://dotnetfiddle.net/MXP7wE

+1

Ja.Es ist wichtig zu erkennen, dass Ausdrucksbäume unveränderlich sind. Um also einen Teil eines Ausdrucksbaums zu ändern, müssen Sie eine vollständige Kopie dieses Ausdrucksbaums erstellen, wobei wichtige Teile davon geändert werden, was der Zweck eines ExpressionVisitors ist. – StriplingWarrior

+1

Heiliger Mist, genial Sergey. Gut zu wissen, dass ich in der Nähe war :) –

1

Sie müssen nur auf einen fix ein paar Dinge:

  • Rückgabetyp Ihrer Methode Expression<Func<T, bool>> sein sollte.
  • Der erste Parameter zu Expression.Call() sollte einfach accessor.Body sein.
  • Der ParameterExpression Parameter zum Methodenaufruf Expression.Lambda<Func<T, bool>>() sollte einfach vom Parameter accessor eingestellt werden.

Methode:

Expression<Func<T, bool>> CreateContains<T>(Expression<Func<T, string>> accessor) 
{ 
    var stringContains = typeof(String).GetMethod("Contains", new[] { typeof(String) }); 
    return Expression.Lambda<Func<T, bool>>(
     Expression.Call(
      accessor.Body, 
      stringContains, 
      Expression.Constant("foo") 
     ) 
     , accessor.Parameters[0] 
    ); 
} 
+0

Hast du einen Blick auf meine Bearbeitung geworfen? [Hier ist die ganze Sache, die ich versuche zu tun] (https://dotnetfiddle.net/wo1Cfi) –

Verwandte Themen