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

Dynamically construct where clause with Func<T, string> lambda - linq to entities

(这是针对 .Net Framework 4.7 的)

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

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

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

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

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

所以这是我当前抛出异常的代码(目前不处理“匹配”参数,我正在实现一个“等于”调用。会有其他人喜欢,开头等等)

异常只是其中一种“类型不匹配”,其函数无法作为参数传递给字符串等式或其他类型。

    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 而不是仅仅生成它的那个

    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 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, criteria_value, (r) => r.SomeProperty.Name);

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

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

谢谢,

请注意,“ReplaceParameter”扩展方法来自此处:

所以,您正在尝试合并您的原型 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 参数,而不是作为字符串常量内联。但是你的问题有点难以理解。