LINQ表达式中不区分大小写的字符串比较

本文关键字:字符串 比较 大小写 表达式 不区 LINQ | 更新日期: 2023-09-27 18:20:53

我正试图编写一个ExpressionVisitor来包装我的LINQ到对象表达式,以自动使它们的字符串比较不区分大小写,就像它们在LINQ到实体中一样。

编辑:我确实想使用ExpressionVisitor,而不仅仅是在创建表达式时对其应用一些自定义扩展或其他内容,原因很重要:传递给我的ExpressionVisitors的表达式是由ASP.Net Web API ODATA层生成的,所以我无法控制它是如何生成的(即,除了在ExpressionVisitor中,我不能将它搜索的字符串小写)。

必须支持LINQ to Entity。不仅仅是扩展。

这是我迄今为止所拥有的。它在字符串上查找对"Contains"的调用,然后在该表达式内的任何成员访问上调用ToLower。

然而,它不起作用。如果我在修改后查看表达式,它对我来说是正确的,所以我不确定我可能做错了什么。

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);
    }

如果我在VisitMember方法的"return expression"行上设置断点,然后在"node"answers"expression"变量上执行"ToString",则断点会被击中两次,下面是两组值:

首次命中:

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

第二次命中:

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()"

我对表情的了解还不够,无法弄清楚我在这一点上做错了什么。有什么想法吗?

LINQ表达式中不区分大小写的字符串比较

我用你的代码制作了一个示例应用程序,它似乎可以工作:

    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);
    }
}

您可以创建这样的扩展方法:

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

然后你可以这样打电话:

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