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

Jos*_*uch 6 c# linq lambda expression-trees expressionvisitor

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

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

必须支持LINQ to Entities.不只是扩展.

这是我到目前为止所拥有的.它在字符串上查找对"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);
    }
Run Code Online (Sandbox Code Playgroud)

如果我在VisitMember方法的"返回表达式"行上设置断点,然后在"节点"和"表达式"变量上执行"ToString",则断点会被击中两次,这里的两组值是:

第一击:

node.ToString()
"$it.LastName"
expression.ToString()
"$it.LastName.ToLower()"
Run Code Online (Sandbox Code Playgroud)

第二击:

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()"
Run Code Online (Sandbox Code Playgroud)

我不太了解表达式来弄清楚我现在做错了什么.有任何想法吗?

Ben*_*Ben 3

我根据您的代码制作了一个示例应用程序,它似乎有效:

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

    }
}
Run Code Online (Sandbox Code Playgroud)