为何传递表达式对象给Where与直接写Lambda表达式结果不同?
问题分析与解决方案
你的问题出在动态构建表达式树时,错误地将成员表达式(member)包装成了常量,导致EF无法正确解析查询条件,最终生成了WHERE 0 = 1的无效语句。
问题代码的核心错误
在构建ToString调用的这一行:
MethodCallExpression memberToString = Expression.Call(Expression.Constant(member), member.GetType().GetMethod("ToString", Type.EmptyTypes));
这里的Expression.Constant(member)是把代表m.Cost的表达式节点本身当成了常量值传递,而不是获取m.Cost属性的值后调用ToString。相当于你生成的表达式逻辑变成了:
m => [表达式节点对象].ToString().ToLower().Contains("67.5")
这种逻辑完全不符合预期,EF无法解析这种无效表达式,就直接判定条件永远不成立,所以生成了WHERE 0 = 1的语句。
修正后的ContainsPredicate方法
把ToString的调用改为直接针对成员表达式,而非将其包装成常量:
public static Expression<Func<T, bool>> ContainsPredicate<T>(string memberName, string searchValue) { var parameter = Expression.Parameter(typeof(T), "m"); var member = Expression.PropertyOrField(parameter, memberName); // 正确调用成员的ToString方法:直接使用member表达式,无需包装成Constant MethodCallExpression memberToString = Expression.Call(member, "ToString", Type.EmptyTypes); MethodCallExpression memberToLower = Expression.Call(memberToString, "ToLower", null); // 显式获取string.Contains方法,避免查找歧义 var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var body = Expression.Call(memberToLower, containsMethod, Expression.Constant(searchValue)); return Expression.Lambda<Func<T, bool>>(body, parameter); }
额外优化提示
如果Cost是数值类型(比如decimal),直接调用它的ToString可能会出现格式差异问题。你可以考虑在调用ToString时传入格式参数,或者改用Convert.ToString的表达式调用,确保生成的字符串和手动写代码时的逻辑完全一致。
验证修正效果
修正后,生成的表达式树和你手动编写的m => m.Cost.ToString().ToLower().Contains("67.5")完全一致,EF就能正确解析并生成对应的SQL查询,返回预期的6条数据了。
内容的提问来源于stack exchange,提问作者Hassan Adel
相关产品推荐
相关产品推荐

