2013-07-02 7 views
6

Ich versuche einen ExpressionVisitor zu schreiben, der meine LINQ-zu-Objekt-Ausdrücke umschließt, damit ihre Zeichenfolgenvergleiche unabhängig von der Groß-/Kleinschreibung in LINQ-to-entities erfolgen.Groß-/Kleinschreibung in LINQ-Ausdruck vergleichen

EDIT: Ich möchte definitiv eine ExpressionVisitor statt nur einige benutzerdefinierte Erweiterung oder etwas auf meinen Ausdruck anwenden, wenn es aus einem wichtigen Grund erstellt: Der Ausdruck an meine ExpressionVisitor wird von der ASP.Net Web API generiert ODATA-Ebene, also habe ich keine Kontrolle darüber, wie sie erzeugt wird (dh ich kann die gesuchte Zeichenkette nur in diesem ExpressionVisitor nicht klein schreiben).

Muss LINQ zu Entities unterstützen. Nicht nur Erweiterung.

Hier ist, was ich bisher habe. Es sucht nach einem Aufruf von "Contains" in einer Zeichenfolge und ruft dann ToLower für jeden Mitgliedszugriff innerhalb dieses Ausdrucks auf.

Allerdings funktioniert es nicht. Wenn ich die Ausdrücke nach meinen Änderungen ansehe, sieht das für mich richtig aus, also bin ich mir nicht sicher, was ich falsch machen könnte.

public class CaseInsensitiveExpressionVisitor : ExpressionVisitor 
{ 

    protected override Expression VisitMember(MemberExpression node) 
    { 
     if (insideContains) 
     { 
      if (node.Type == typeof (String)) 
      { 
       var methodInfo = typeof (String).GetMethod("ToLower", new Type[] {}); 
       var expression = Expression.Call(node, methodInfo); 
       return expression; 
      } 
     } 
     return base.VisitMember(node); 
    } 

    private Boolean insideContains = false; 
    protected override Expression VisitMethodCall(MethodCallExpression node) 
    { 
     if (node.Method.Name == "Contains") 
     { 
      if (insideContains) throw new NotSupportedException(); 
      insideContains = true; 
      var result = base.VisitMethodCall(node); 
      insideContains = false; 
      return result; 
     } 
     return base.VisitMethodCall(node); 
    } 

Wenn ich einen Haltepunkt auf der „Rückkehr Ausdruck“ Zeile in der VisitMember Methode festgelegt und dann ein „ToString“ auf dem „Knoten“ und „Ausdruck“ Variablen, wird der Haltepunkt zweimal getroffen, und hier ist was die beiden Sätze von Werten sind:

Erster Treffer:

node.ToString() 
"$it.LastName" 
expression.ToString() 
"$it.LastName.ToLower()" 

Zweiter Treffer:

node.ToString() 
"value(System.Web.Http.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.String]).TypedProperty" 
expression.ToString() 
"value(System.Web.Http.OData.Query.Expressions.LinqParameterContainer+TypedLinqParameterContainer`1[System.String]).TypedProperty.ToLower()" 

Ich weiß nicht genug über Ausdrücke, um herauszufinden, was ich an diesem Punkt falsch mache. Irgendwelche Ideen?

+1

'string.Equals (string1, string2, StringComparison.InvariantCultureIgnoreCase)'? – Corak

+1

Vermeiden Sie 'ToLower' für den Zeichenfolgenvergleich, da es mit höherer Wahrscheinlichkeit zu einem Fehler kommt ([Puten Test] (http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html))). Verwenden Sie entweder Großbuchstaben oder vorzugsweise, wie Corak vorgeschlagen hat, String.Equals. – keyboardP

+0

Dies funktioniert in meinem Fall nicht. Erstens habe ich keine Kontrolle über den Ausdruck, da er automatisch von der ASP.Net-Web-API generiert wird. Zweitens möchte ich etwas, das ich generisch verwenden kann, um eine LINQ-Anweisung zu umbrechen und wird sowohl mit LINQ-to-Entities als auch mit LINQ-to-Objects arbeiten. –

Antwort

2

ich eine Beispielanwendung aus dem Code gemacht und es scheint zu funktionieren:

public class Test 
{ 
    public string Name; 
} 
public class CaseInsensitiveExpressionVisitor : ExpressionVisitor 
{ 

    protected override Expression VisitMember(MemberExpression node) 
    { 
     if (insideContains) 
     { 
      if (node.Type == typeof (String)) 
      { 
       var methodInfo = typeof (String).GetMethod("ToLower", new Type[] {}); 
       var expression = Expression.Call(node, methodInfo); 
       return expression; 
      } 
     } 
     return base.VisitMember(node); 
    } 

    private Boolean insideContains = false; 

    protected override Expression VisitMethodCall(MethodCallExpression node) 
    { 
     if (node.Method.Name == "Contains") 
     { 
      if (insideContains) throw new NotSupportedException(); 
      insideContains = true; 
      var result = base.VisitMethodCall(node); 
      insideContains = false; 
      return result; 
     } 
     return base.VisitMethodCall(node); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Expression <Func<Test, bool>> expr = (t) => t.Name.Contains("a"); 
     var expr1 = (Expression<Func<Test, bool>>) new CaseInsensitiveExpressionVisitor().Visit(expr); 
     var test = new[] {new Test {Name = "A"}}; 
     var length = test.Where(expr1.Compile()).ToArray().Length; 
     Debug.Assert(length == 1); 
     Debug.Assert(test.Where(expr.Compile()).ToArray().Length == 0); 

    } 
} 
+0

Hmm .... könnte es etwas mit dem wirklich langen Ausdruck zu tun haben enthält enthält (am Ende meiner Frage notiert): "Wert (System.Web.Http.OData.Query.Expressions.LinqParameterContainer + TypedLinqParameterContainer'1 [System.String]). TypedProperty ". Ich weiß nicht, was es ist, aber ich verwende immer noch ToLower, da es ein String-Typ ist. –

+0

Oder könnte es etwas damit zu tun haben, dass LINQ to Entities den Ausdruck ausführt? Wenn ich versuche, den Ausdruck direkt zu kompilieren/aufrufen, erhalte ich die Meldung "Diese Methode unterstützt die Infrastruktur von INQ to Entities und ist nicht dafür gedacht, direkt von Ihrem Code verwendet zu werden." –

+0

Ich bin mit OData nicht vertraut. – Ben

0

können Sie eine extesion Methode wie folgt erstellen:

public static class Extensions 
{ 
    public static bool InsensitiveEqual(this string val1, string val2) 
    { 
     return val1.Equals(val2, StringComparison.OrdinalIgnoreCase); 
    } 
} 

Und dann können Sie wie folgt aufrufen:

string teste = "teste"; 
string teste2 = "TESTE"; 

bool NOTREAL = teste.Equals(teste2); //FALSE 
bool REAL = teste.InsensitiveEqual(teste2); //true 
+0

Danke, aber das wird nicht funktionieren. Ich möchte einen ExpressionVisitor verwenden, damit ich meine LINQ-Anweisung so umschließen kann, dass ich sie sowohl in LINQ-to-Objects als auch in LINQ-to-Entities verwenden kann. Eine benutzerdefinierte Erweiterung InsensitiveEqual() wird in LINQ-to-Entities nicht funktionieren. –

+0

Außerdem habe ich keine Kontrolle über die Erstellung des Ausdrucks, da die ASP.Net-Web-API-Odata-Ebene den Ausdruck generiert. –