2016-04-19 6 views
0

Ich folge diesem SO answer, um Lambda-Ausdrücke in partielle SQL-Syntax zu konvertieren.Erstellen eines Ausdrucksbaums für Enthält <T>

Ich habe jedoch Probleme beim Analysieren des Ausdrucks für Contains. Ich habe eine Methode:

private bool ParseContainsExpression(MethodCallExpression expression) 
{ 
    MemberExpression member = (MemberExpression)expression.Arguments[0]; 

    var methodInfo = typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) }); 

    //TODO check if list contains value 

    return false; 
} 

Als ich Ausdrücke völlig neu bin, ich weiß nicht, woher der Name der Eigenschaft erhalten und Wert, und die Liste, die die Werte enthält, möchte ich gegen überprüfen. Wo sind diese Eigenschaften und Werte im Ausdruck gespeichert?

Antwort

1

Grundsätzlich müssen Sie den Method, Object (Ausdruck verarbeiten, die die Instanz zum Beispiel Methode aufruft oder null für statische Methodenaufrufe darstellt) und Arguments (eine Sammlung von Ausdrücken, die Argumente der aufgerufenen Methode darstellen) Eigenschaften der MethodCallExpression Klasse.

Speziell für Contains, müssen Sie vermeiden (oder Prozess anders, wenn nötig) die string.Contains Methode, und auch static Methoden wie Enumerable.Contains sowie Instanzmethoden wie ICollection<T>.Contains, List<T>.Contains usw. Um die Liste Werte verarbeiten zu bekommen (wenn möglich), müssen Sie einen konstanten Ausdruck finden. Hier

ist ein Beispiel:

private bool ParseContainsExpression(MethodCallExpression expression) 
{ 
    // The method must be called Contains and must return bool 
    if (expression.Method.Name != "Contains" || expression.Method.ReturnType != typeof(bool)) return false; 
    var list = expression.Object; 
    Expression operand; 
    if (list == null) 
    { 
     // Static method 
     // Must be Enumerable.Contains(source, item) 
     if (expression.Method.DeclaringType != typeof(Enumerable) || expression.Arguments.Count != 2) return false; 
     list = expression.Arguments[0]; 
     operand = expression.Arguments[1]; 
    } 
    else 
    { 
     // Instance method 
     // Exclude string.Contains 
     if (list.Type == typeof(string)) return false; 
     // Must have a single argument 
     if (expression.Arguments.Count != 1) return false; 
     operand = expression.Arguments[0]; 
     // The list must be IEnumerable<operand.Type> 
     if (!typeof(IEnumerable<>).MakeGenericType(operand.Type).IsAssignableFrom(list.Type)) return false; 
    } 
    // Try getting the list items 
    object listValue; 
    if (list.NodeType == ExpressionType.Constant) 
     // from constant value 
     listValue = ((ConstantExpression)list).Value; 
    else 
    { 
     // from constant value property/field 
     var listMember = list as MemberExpression; 
     if (listMember == null) return false; 
     var listOwner = listMember.Expression as ConstantExpression; 
     if (listOwner == null) return false; 
     var listProperty = listMember.Member as PropertyInfo; 
     listValue = listProperty != null ? listProperty.GetValue(listOwner.Value) : ((FieldInfo)listMember.Member).GetValue(listOwner.Value); 
    } 
    var listItems = listValue as System.Collections.IEnumerable; 
    if (listItems == null) return false; 

    // Do whatever you like with listItems 

    return true; 
} 
2

Ihre Implementierung unterscheidet sich ziemlich von der Beispielantwort. Sie müssen wirklich von ExpressionVisitor erben, damit Sie den Baum richtig analysieren können.

wir diesen Ausdruck für ein Beispiel nehmen:

var myList = new List<string> { "A" }; 
Expression<Func<string, bool>> a = (s) => myList.Contains(s); 
ParseContainsExpression(a.Body as MethodCallExpression); 

private bool ParseContainsExpression(MethodCallExpression expression) 
{ 
    expression.Object; //myList 
    expression.Arguments[0]; //s  
    return false; 
} 

Hinweis aber diese sind noch Ausdrücke, sind sie noch nicht Istwerten. Sie müssen den Ausdruck aufrufen, um die Werte abzurufen. In unserem Fall - myList ist eigentlich ein ConstantExpression. So können wir das tun:

((expression.Object as MemberExpression).Expression as ConstantExpression).Value; //myList 

Das gibt uns die ursprüngliche Liste zurück. Beachten Sie, dass es ein Feld Zugriff ist, da der Ausdruck in eine Closure kompiliert wird, die myList als ein Feld in der Closure-Klasse setzt. Wie Sie sehen können, müssen wir ein Los der Annahmen über den Typ des Ausdrucks machen, den wir bearbeiten (dass es ein Feldzugriff und dann ein konstanter Ausdruck ist). Sie müssen wirklich eine vollwertiges Besucher Umsetzung dieses richtig machen (was die verknüpfte Antwort beschreibt)

+0

Ja, ich bin mit der vollständigen Umsetzung Besucher wie in der verknüpften Antwort beschrieben, aber ich mag 'Contains', um es auch zu addieren. –

+1

Rob sagt, dass Sie keine Reflexion brauchen, Sie müssen nur die Liste und den Kandidaten aus dem Ausdruck holen und dann enthält der Aufruf direkt auf der Liste. – briantyler

+0

In dem Namespace finde ich 'Dump()' –

Verwandte Themen