我正在尝试编写一个 ExpressionVisitor 来包装我的 LINQ-to-object 表达式,以自动使其字符串比较不区分大小写,就像它们在 LINQ-to-entities 中一样。
编辑:我绝对想使用 ExpressionVisitor 而不仅仅是在创建表达式时应用一些自定义扩展或其他东西,原因有一个:传递给我的 ExpressionVisitor 的表达式是由 ASP.Net Web API ODATA 层生成的,所以我无法控制它是如何生成的(即我不能小写它正在搜索的字符串,除非在这个 ExpressionVisitor 中)。
必须支持 LINQ to Entities。不仅仅是扩展。
这是我到目前为止所拥有的。它在字符串上查找对“包含”的调用,然后对该表达式内的任何成员访问调用 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 方法的“返回表达式”行上设置断点,然后在“节点”和“表达式”变量上执行“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()"
我对表达式的了解还不够,无法弄清楚我在这一点上做错了什么。有任何想法吗?