Groß- / Kleinschreibung-abhängige Zeichenfolge im LINQ-Ausdruck vergleichen

c# expression-trees expressionvisitor lambda linq

Frage

Ich versuche, einen ExpressionVisitor so zu schreiben, dass er meine LINQ-zu-Objekt-Ausdrücke umschließt, damit ihre String-Vergleiche automatisch genauso wie in LINQ-to-Entities von Groß- und Kleinschreibung unterschieden werden.

BEARBEITEN: Ich möchte definitiv einen ExpressionVisitor verwenden, anstatt nur eine benutzerdefinierte Erweiterung oder etwas auf meinen Ausdruck anzuwenden, wenn er aus einem wichtigen Grund erstellt wird: Der Ausdruck, der an meinen ExpressionVisitor übergeben wird, wird von der ODATA-Ebene der ASP.Net-Web-API generiert Ich habe keine Kontrolle darüber, wie es generiert wird (dh ich kann die gesuchte Zeichenfolge nicht in Kleinbuchstaben eingeben, außer in diesem ExpressionVisitor).

Muss LINQ zu Entitäten 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.

Es funktioniert jedoch 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 in der VisitMember-Methode einen Haltepunkt für die Zeile "return expression" festlege und dann für die Variablen "node" und "expression" einen "ToString" verwende, wird der Haltepunkt zweimal getroffen, und hier sind die beiden Werte :

Erster Treffer:

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

Zweiter Hit:

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?

Akzeptierte Antwort

Ich habe eine Beispiel-App aus deinem Code erstellt 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);

    }
}

Beliebte Antwort

Sie können eine Erweiterungsmethode wie folgt erstellen:

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

Und dann kannst du so anrufen:

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

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


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow