首页 > 解决方案 > 为什么将表达式对象传递给 where 会返回与输入 lambda 表达式不同的结果?

问题描述

我有以下代码正确返回六个对象的列表

var items = db.items.take(100);
 var result = items.Where(m => m.Cost.ToString().ToLower().Contains("67.5")).ToList(); //returns 6 items

我正在尝试使用动态表达式做同样的事情。

// Print out the expression.
// .ToString() returns "m => m.Cost.ToString().ToLower().Contains("67.5")"
var whereClause = ContainsPredicate<item>("Cost", "67.5"); 
var result = items.Where(whereClause).ToList(); //returns 0 items

当我尝试使用 sql profiler 查看发送到数据库的内容时,我注意到它删除了我的子句并添加了WHERE 0 = 1

ContainsPredicate 方法实现:

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

            MethodCallExpression memberToString = Expression.Call(Expression.Constant(member), member.GetType().GetMethod("ToString", Type.EmptyTypes));

            MethodCallExpression memberToLower = Expression.Call(memberToString,"ToLower", null);

            var body = Expression.Call(memberToLower,"Contains",Type.EmptyTypes,Expression.Constant(searchValue));

            return Expression.Lambda<Func<T, bool>>(body, parameter);
        }

任何建议表示赞赏。谢谢。

标签: c#.netlinqlambdaexpression-trees

解决方案


public static void Test()
{
    var myItem = new Item() { Cost = 67.5 };
    var items = new List<Item> { myItem };

    var result = items.Where(m => 
    m.Cost.ToString().ToLower().
    Contains("67,5")).ToList();

    var whereClause = ContainsPredicate<Item>("Cost", "67,5");

    // var test1 = whereClause(myItem);
    var result2 = items.Where(whereClause).ToList(); // returns 1 result in my case
}

public static Func<T, bool> ContainsPredicate<T>(string memberName, string searchValue)
{
    var parameter = Expression.Parameter(typeof(T), "m");
    var member = Expression.PropertyOrField(parameter, memberName);

    // Mistake was here:
    var doubleToStr = member.Type.GetMethod("ToString", Type.EmptyTypes);
    MethodCallExpression memberToString = Expression.Call(member, doubleToStr);

    MethodCallExpression memberToLower = 
        Expression.Call(memberToString, "ToLower", null);

    var body = Expression.Call(memberToLower, "Contains", Type.EmptyTypes
        , Expression.Constant(searchValue));

    var lamb = Expression.Lambda<Func<T, bool>>(body, parameter);

    // we need to compile
    return lamb.Compile();
}

我做的第一件事只是编译memberToLower并返回字符串“m.cost”而不是你的双精度。“m.cost”显然永远不会包含“67.5”。所以你去。

你确定你不想要这样的东西:

public static Func<T, bool> ContainsPredicate2<T>(string memberName, string searchValue)
{
    var prop = typeof(T).GetProperty(memberName);

    Func<T, bool> func = (T obj2) =>
        prop.GetValue(obj2).ToString().ToLower().Contains(searchValue);

    return func;
}

推荐阅读