2016-06-19 11 views
0

Ich versuche festzustellen, ob eine MemberExpression zu einer Objektinstanz gehört oder ob es nur ein Parameter in einem Ausdruck ist. Ich kam auf, was ich für eine brauchbare Lösung halte, aber ich möchte dies zuerst mit SE ausführen. Nehmen Sie diese beiden Ausdrücke für den Anfang:Ermitteln, ob eine MemberExpression zu einer Instanz gehört

var myClassInstance = new MyClass(); 

Expression<Func<MyClass, bool>> exp1 = mc => myClassInstance.MyBool; 
Expression<Func<MyClass, bool>> exp2 = mc => mc.MyBool; 

exp1 enthält einen Verweis auf eine lokale Variable/Feld. Weiter unten in meinem Code wird .MyBool kompiliert werden, um dessen Wert wie folgt abzurufen:

Expression.Lambda<Func<object>>(Expression.Convert(exp1.Body as MemberExpression, typeof(object))).Compile(); 

In exp2, .MyBool nicht von einer Objektreferenz, sondern ein paremeter. Wenn ich versuchen würde, den Ausdruck .MyBool zu kompilieren, würde die Ausnahme variable 'mc' of type 'ConsoleLol.MyClass' referenced from scope '', but it is not defined ausgelöst werden.

Nach einiger Mühe, ich habe mit dieser potentiellen Lösung zu kommen:

var exp1parent = (exp1.Body as MemberExpression).Expression; 
var exp2parent = (exp2.Body as MemberExpression).Expression; 

Console.WriteLine(exp1parent.NodeType); 
Console.WriteLine(exp2parent.NodeType); 

Es scheint, dass, wenn die Mutter Ausdruck Ausdruck keine Instanz ist, wird die NodeType Eigenschaft Parameter sein. Ansonsten wird es in diesem Fall etwas anderes sein, in diesem Fall MemberAccess.

Bin ich in dieser Schlussfolgerung richtig oder gibt es eine bessere Methode, diese Prüfung durchzuführen?

+0

Können Sie erklären, was Sie eigentlich versuchen? Warum ist der Ausdruck 'Expression >', wenn Sie scheinbar nicht 'MyClass' haben, um ihn zu übergeben? Warum verwenden Sie nicht den Ausdruck > ', der all dies vermeiden würde? – svick

+0

@svick Nicht sicher, was du meinst. 'Func ' definiert eine 'MyClass' als Parameter und gibt ein' bool' zurück. Außerdem brauche ich 'MyClass', weil ich damit einen Ausdruck in SQL umwandle.Ich muss wissen, ob der Ausdruck zu einem Instanzmitglied gehört, so dass ich bestimmen kann, ob es kompiliert werden soll oder nicht, um seinen Wert zu erhalten oder den Ausdruck in eine benutzerdefinierte SQL-Funktion zu konvertieren. Schwer zu erklären, alles im Detail mit begrenztem Raum – oscilatingcretin

Antwort

0

Wenn ich Sie richtig verstehe, bauen Sie einen SQL-Provider und Sie müssen alles auswerten, was nicht in SQL übersetzt werden kann. Eine Erklärung, wie man das macht, ist in Matt Warrens Artikel Building an IQueryable Provider – Part III oder Sie können es als PartialEvaluator von seiner iqtoolkit Bibliothek verwenden.

+0

Ich habe schon ziemlich viel von all dem gemacht, indem ich eine Methode, die den Ausdruck Baum rezesiert. Wie ich in einem Kommentar in meinem OP gesagt habe, ist es schwierig zu erklären, was ich wirklich tun möchte, aber ich muss wissen, ob ich ein Mitglied oder eine Methode kompiliere oder das Mitglied in einen benutzerdefinierten SQL-Ausdruck umwandeln soll (wie 'mc .FirstName.Length == 10' bis 'Länge (Vorname) = 10' in meinem SQL) – oscilatingcretin

0

Op hier mit einer Antwort auf meine eigene Frage.

Dies kann ein Werk in Arbeit sein, aber es funktioniert so weit. Wenn Sie Ihren Ausdrucksbaum durchlaufen und versuchen zu entscheiden, ob ein Eigenschaftsausdruck als Datenbankfeld interpretiert werden soll oder ob er für den SQL-Wertparameter kompiliert werden soll, müssen Sie wissen, ob der Ausdruck von einem Lambda-Ausdrucksparameter abstammt. Wenn dies nicht der Fall ist und Sie versuchen, es zu kompilieren/aufrufen, erhalten Sie den in meinem OP genannten Fehler.

Hier ist, was ich für den Anfang habe. Sie hüpfen im Grunde nur so lange in den Ausdrucksbaum, bis der übergeordnete Ausdruck NodeType == ExpressionType.Parameter oder der übergeordnete Ausdruck null ist. Wenn der erste, ist es ein Parameter, der absteigend ist, also kompilieren Sie es nicht. Wenn letzteres ein Datenbankfeld darstellt, so parse es entsprechend.

public static bool IsParameterDescendent(this Expression Expression) 
{ 
    var pe = Expression; 

    while (pe != null && pe.NodeType != ExpressionType.Constant) 
    { 
     if (pe.NodeType == ExpressionType.MemberAccess) 
     { 
      pe = pe.TryCast<MemberExpression>().Expression; 
     } 
     else if (pe.NodeType == ExpressionType.Call) 
     { 
      pe = pe.TryCast<MethodCallExpression>().Object; 
     } 

     if (pe?.NodeType == ExpressionType.Parameter) return true; 
    } 

    return false; 
} 

Edit: Eine Möglichkeit, diese zusammen mit könnte vermeiden zu tun ist, sich selbst zu disziplinieren immer um die Datenbank zu setzen Eigenschaft auf der linken Seite des Operators und der aufrufbaren/Wert-Provider auf der rechten Seite. Ich mag es jedoch nicht, darauf beschränkt zu sein, daher diese Methode.

Verwandte Themen