使用 Func<T, string> lambda 动态构造 where 子句 - linq to entity

问题描述

(这是针对 .Net Framework 4.7)

我正在尝试编写一些扩展方法来帮助为各种实体创建动态 where 子句。我是几天前开始的,所以可能有很多我不知道的,还有一些我可能误解了。

我已经设法创建了一种扩展方法,用于按 1 个属性进行过滤,它按我的预期工作(我确实使用反射来获取属性,但无法使其与接口一起使用 - 好吧,没有它执行 sql那是)。 不过,我似乎无法让这个用于 lambda 表达式。

注意,解决方案一定不能触发sql执行。因为我能够写出一些“有效”的变体,但它们会触发 sql 执行。

我的工作方式是,一旦我准备好代码,我就开始调试并在手表中进行“查询”。它看起来像这样(注意sql代码

watched-query-sql

一旦我跳过我的 FilterString 方法调用,它要么变成一个 sql 结果,要么我得到一个异常(使用当前代码),它不应该:

exception

所以这是我当前抛出异常的代码(目前不处理“match”参数,我正在实现“equals”调用。还有其他类似、开头、类似等)

异常只是那些“类型不匹配”中的一种,函数不能作为参数传递给字符串 Equals 或其他什么。

    public static IQueryable<T> FilterString<T>(this IQueryable<T> query,Match match,string criteriaItem,Expression<Func<T,string>> getItemString)
        where T : class
    {
        if (string.IsNullOrEmpty(criteriaItem))
        {
            return query;
        }

        var param = Expression.Parameter(typeof(T),"r");
        var selector = Expression.Lambda<Func<T,string>>(getItemString,param);
        Expression<Func<string,bool>> prototype = item => item == criteriaItem;
        var predicate = Expression.Lambda<Func<T,bool>>(
            prototype.Body.ReplaceParameter(prototype.Parameters[0],selector.Body),selector.Parameters[0]);

        return query.Where(predicate);
    }

以及执行 sql 而不是仅生成它的那个

sql-executed

    public static IQueryable<T> FilterString<T>(this IQueryable<T> query,"r");
        //var value = Expression.Constant(getItemString);
        var equals = typeof(string).getmethod("Equals",new Type[] { typeof(string) });
        var item = Expression.Invoke(getItemString,param);
        var body = Expression.Call(Expression.Constant(criteriaItem),equals,item);

        return query.Where(Expression.Lambda<Func<T,bool>>(body,param));
    }

调用这些是这样完成的

query = query.FilterString(match,criteria_value,(r) => r.someProperty.MaybeSomeOtherProp.someString);
query = query.FilterString(match,(r) => r.someProperty.Name);

这个相同的扩展方法将在任意数量的不同实体上调用,具有任意数量的不同属性和道具名称。我想我可以利用我工作的反射版本并在某种数组中传递所有属性名称,但这简直太丑了。

长话短说,我怎样才能按照我上面解释的方式工作,就是:生成 sql 而不是执行?

谢谢,

注意,“ReplaceParameter”扩展方法是这里的一个https://stackoverflow.com/a/39206392/630515

解决方法

因此,您正在尝试合并您的原型 item => item == criteriaItem。使用传入的字符串属性表达式,例如 (r) => r.SomeProperty.Name 来创建 (r) => r.SomeProperty.Name == criteriaItem

    Expression<Func<string,bool>> prototype = item => item == criteriaItem;
    var predicate = Expression.Lambda<Func<T,bool>>(
        ReplacingExpressionVisitor.Replace(
            prototype.Parameters[0],getItemString.Body,prototype.Body),getItemString.Parameters[0]);

而且我认为您正在尝试这样做,以便将 criteriaItem 绑定到 sql 参数,而不是作为字符串常量内联。但是你的问题有点难以理解。