首页 > 解决方案 > 如何为 linq / EF Core 中的 where 语句编写动态谓词构建器?

问题描述

我正在处理IQueryable<SomeRandomObject>使用 EF Core 3.1 数据上下文提取的数据。

我很确定我可以动态地构建一个谓词,.Where()这样我就可以为哪一列和什么值传递一个字符串。

当然这不起作用,但一些伪代码可能是:

IQueryable myQueryable = stuffFromContext;
var columnName = "memberid";
var searchValue = "1234";
var results = myQueryable.Where(x=> someMagicColumnFunction(columnName, searchvalue))

在这一点上我只做过研究,谓词构建不是我的专业领域。

有人可以帮我创建一个函数,我可以传入参数 my IQueryable,一个表示列名的字符串和一个用于搜索的字符串(现在完全相等,没有'like')。

我很想看看这是怎么做到的。我在任何地方都找不到一个可靠的例子来说明如何做这样的小事。大多数例子都是一切和厨房水槽!

标签: c#linqef-core-3.1

解决方案


假设行的类型是,myQueryable那么TQueryable您可以创建一个myQueryable特定的函数来生成 lambda:

Expression<Func<TQueryable, bool>> EqualsFilter<TCol>(string columnName, TCol searchValue) {
    // build x => x.{columnName} == searchValue

    // (TQueryable x)
    var xParam = Expression.Parameter(typeof(TQueryable), "x");
    // x.{columnName}
    var colExpr = Expression.Property(xParam, columnName);
    // {searchValue}
    var constExpr = Expression.Constant(searchValue);
    // x.{columnName} == {searchValue}
    var lambdaBody = Expression.MakeBinary(ExpressionType.Equal, colExpr, constExpr);
    // (TQueryable x) => x.{columnName} == {searchValue}
    var lambda = Expression.Lambda<Func<TQueryable, bool>>(lambdaBody, xParam);

    return lambda;
}

一旦你有了这个方法,你可以像这样使用它:

var myQueryable = stuffFromContext;
var columnName = "memberid";
var searchValue = "1234";
var results = myQueryable.Where(EqualsFilter(columnName, searchvalue));

但是,如果myQueryable具有复杂或匿名类型(由于 a Selector Join),则需要替换Where为 C# 只能从参数推断类型,因此您需要myQueryable参数来获取要过滤的实体类型。使用通用版本的EqualsFilter作为辅助方法,您有:

public static class IQueryableExt {
    static Expression<Func<T, bool>> EqualsFilter<T, TCol>(string columnName, TCol searchValue) {
        // build x => x.{columnName} == searchValue

        // (T x)
        var xParam = Expression.Parameter(typeof(Accounts), "x");
        // x.{columnName}
        var colExpr = Expression.Property(xParam, columnName);
        // {searchValue}
        var constExpr = Expression.Constant(searchValue);
        // x.{columnName} == {searchValue}
        var lambdaBody = Expression.MakeBinary(ExpressionType.Equal, colExpr, constExpr);
        // (T x) => x.{columnName} == {searchValue}
        var lambda = Expression.Lambda<Func<T, bool>>(lambdaBody, xParam);

        return lambda;
    }

    public static IQueryable<T> WhereColumnEquals<T, TCol>(this IQueryable<T> src, string columnName, TCol searchValue)
        => src.Where(EqualsFilter<T, TCol>(columnName, searchValue));
}

您现在可以使用如下:

var myQueryable = stuffFromContext;
var columnName = "memberid";
var searchValue = "1234";
var results = myQueryable.WhereColumnEquals(columnName, searchvalue);

推荐阅读