You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于List<object>的Entity Framework动态Where子句实现问题

基于WhereClause列表生成Entity Framework动态Where子句的实现方案

我之前也做过类似的用户自定义查询需求,这里给你两种经过项目验证的可行实现思路,你可以根据场景选择:

方法一:手动构建表达式树(推荐,类型安全)

这种方式直接用.NET的表达式树API拼接查询条件,完全类型安全,还能避免SQL注入风险——EF会自动把表达式转换成参数化的SQL语句。

先写一个工具类封装表达式构建逻辑:

using System.Linq.Expressions;

public static class DynamicQueryBuilder
{
    // 根据实体类型和列名获取属性的表达式节点
    private static MemberExpression GetPropertyExpression<T>(string columnName)
    {
        var parameter = Expression.Parameter(typeof(T), "entity");
        return Expression.PropertyOrField(parameter, columnName);
    }

    // 根据操作符生成对应的比较表达式
    private static Expression GetComparisonExpression(MemberExpression propertyExpr, string op, object value)
    {
        // 把用户输入的值转换成属性对应的类型,避免类型不匹配
        var targetType = propertyExpr.Type;
        var convertedValue = Convert.ChangeType(value, targetType);
        var constantExpr = Expression.Constant(convertedValue, targetType);

        return op switch
        {
            "Equals" => Expression.Equal(propertyExpr, constantExpr),
            "NotEquals" => Expression.NotEqual(propertyExpr, constantExpr),
            "GreaterThan" => Expression.GreaterThan(propertyExpr, constantExpr),
            "GreaterThanOrEqual" => Expression.GreaterThanOrEqual(propertyExpr, constantExpr),
            "LessThan" => Expression.LessThan(propertyExpr, constantExpr),
            "LessThanOrEqual" => Expression.LessThanOrEqual(propertyExpr, constantExpr),
            "Contains" => Expression.Call(
                propertyExpr, 
                typeof(string).GetMethod("Contains", new[] { typeof(string) })!, 
                constantExpr
            ),
            "StartsWith" => Expression.Call(
                propertyExpr, 
                typeof(string).GetMethod("StartsWith", new[] { typeof(string) })!, 
                constantExpr
            ),
            // 可根据需求扩展更多操作符
            _ => throw new NotSupportedException($"操作符「{op}」暂不支持")
        };
    }

    // 把所有WhereClause合并成最终的查询表达式
    public static Expression<Func<T, bool>> BuildWhereExpression<T>(List<WhereClause> clauses)
    {
        if (clauses?.Count == 0)
        {
            // 无过滤条件时返回始终为true的表达式,相当于不做筛选
            return _ => true;
        }

        var parameter = Expression.Parameter(typeof(T), "entity");
        Expression combinedExpr = null;

        foreach (var clause in clauses)
        {
            var propertyExpr = GetPropertyExpression<T>(clause.ColumnName);
            var currentCondition = GetComparisonExpression(propertyExpr, clause.Operator, clause.Value);

            if (combinedExpr == null)
            {
                // 第一个条件直接赋值
                combinedExpr = currentCondition;
            }
            else
            {
                // 根据AndOr拼接逻辑与/或
                combinedExpr = string.Equals(clause.AndOr, "And", StringComparison.OrdinalIgnoreCase)
                    ? Expression.AndAlso(combinedExpr, currentCondition)
                    : Expression.OrElse(combinedExpr, currentCondition);
            }
        }

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

使用示例

假设你的EF实体是Product,DbSet为dbContext.Products,直接调用工具类生成表达式即可:

// 模拟用户通过窗体构建的查询条件
var userClauses = new List<WhereClause>
{
    new WhereClause { ColumnName = "Price", Operator = "GreaterThan", Value = 100, AndOr = "And" },
    new WhereClause { ColumnName = "Category", Operator = "Equals", Value = "Electronics" }
};

// 生成过滤表达式
var filterExpr = DynamicQueryBuilder.BuildWhereExpression<Product>(userClauses);

// 应用到EF查询上
var filteredProducts = dbContext.Products.Where(filterExpr).ToList();

方法二:使用Dynamic LINQ库(灵活快速)

如果更倾向于字符串拼接的方式,System.Linq.Dynamic.Core这个NuGet包可以帮你快速实现动态查询,注意要使用参数化避免注入风险。

首先安装NuGet包:Install-Package System.Linq.Dynamic.Core

然后写一个扩展方法处理条件拼接:

using System.Linq.Dynamic.Core;
using System.Text;

public static class DynamicQueryExtensions
{
    public static IQueryable<T> ApplyDynamicWhere<T>(this IQueryable<T> query, List<WhereClause> clauses)
    {
        if (clauses?.Count == 0)
        {
            return query;
        }

        var conditionParts = new List<string>();
        var parameters = new List<object>();

        foreach (var clause in clauses)
        {
            // 用参数化方式避免SQL注入,参数名用@p0、@p1...
            var paramName = $"@{parameters.Count}";
            var condition = clause.Operator switch
            {
                "Equals" => $"{clause.ColumnName} == {paramName}",
                "NotEquals" => $"{clause.ColumnName} != {paramName}",
                "GreaterThan" => $"{clause.ColumnName} > {paramName}",
                "GreaterThanOrEqual" => $"{clause.ColumnName} >= {paramName}",
                "LessThan" => $"{clause.ColumnName} < {paramName}",
                "LessThanOrEqual" => $"{clause.ColumnName} <= {paramName}",
                "Contains" => $"{clause.ColumnName}.Contains({paramName})",
                _ => throw new NotSupportedException($"操作符「{clause.Operator}」暂不支持")
            };

            conditionParts.Add(condition);
            parameters.Add(clause.Value);
        }

        // 拼接所有条件,第一个条件不需要AndOr
        var fullCondition = new StringBuilder(conditionParts[0]);
        for (var i = 1; i < clauses.Count; i++)
        {
            fullCondition.Append($" {clauses[i].AndOr} {conditionParts[i]}");
        }

        return query.Where(fullCondition.ToString(), parameters.ToArray());
    }
}

使用示例

直接用扩展方法给EF查询添加过滤条件:

var userClauses = new List<WhereClause>
{
    new WhereClause { ColumnName = "Price", Operator = "LessThan", Value = 500, AndOr = "Or" },
    new WhereClause { ColumnName = "InStock", Operator = "Equals", Value = true }
};

var filteredProducts = dbContext.Products.ApplyDynamicWhere(userClauses).ToList();

注意事项

  • 表达式树方法更安全,能在编译时检查列名是否存在,适合对类型安全要求高的场景;
  • Dynamic LINQ更灵活,适合快速实现复杂动态查询,但要确保操作符和列名拼写正确;
  • 若需处理空值(比如用户未输入Value),可在代码中添加判断,跳过空条件或处理IsNull这类特殊操作;
  • 确保WhereClause中的Operator字段与代码中switch分支对应,比如统一用"Equals"而非"=",避免匹配错误。

内容的提问来源于stack exchange,提问作者LCaraway

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:24:46