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

如何通过Lambda表达式实现IQueryable多列筛选 无需为各场景写独立if语句

IQueryable多列动态筛选最优实现方案

你原有实现的核心问题是硬编码枚举所有筛选组合,扩展性极差,还存在逻辑歧义:当SearchColumns同时包含warehouse和date时,前面两个if会先拼接两个AND条件,再叠加第三个if的多列OR条件,最终逻辑和预期的多列模糊匹配大概率不符。

推荐两种落地成本低的优化方案:


方案1:轻量映射方案(适配当前业务场景,无额外依赖)

核心思路是提前定义列名和筛选逻辑的映射关系,动态选中需要的筛选条件后拼接为OR查询,无需写大量if判断。

实现代码

if (!string.IsNullOrWhiteSpace(orderFilter.SearchValue))
{
    // 1. 预设列名和对应筛选逻辑的映射
    var filterMap = new Dictionary<string, Expression<Func<Order, bool>>>
    {
        ["warehouse"] = x => x.Warehouse.Description.Contains(orderFilter.SearchValue),
        ["date"] = x => x.Date.ToString().Contains(orderFilter.SearchValue),
        ["orderno"] = x => x.OrderNo.Contains(orderFilter.SearchValue),
        ["client"] = x => x.Client.Description.Contains(orderFilter.SearchValue),
        ["salesman"] = x => x.Salesman.Username.Contains(orderFilter.SearchValue)
    };

    // 2. 取出选中列对应的筛选表达式
    var selectedFilters = orderFilter.SearchColumns
        .Where(col => filterMap.ContainsKey(col))
        .Select(col => filterMap[col])
        .ToList();

    if (selectedFilters.Any())
    {
        // 3. 把多个筛选表达式拼接为OR条件
        var parameter = Expression.Parameter(typeof(Order), "x");
        var combinedBody = selectedFilters
            .Select(f => Expression.Invoke(f, parameter))
            .Aggregate(Expression.OrElse);
        var combinedLambda = Expression.Lambda<Func<Order, bool>>(combinedBody, parameter);
        
        // 4. 拼接查询条件
        query = query.Where(combinedLambda);
    }
}

优势

  • 改造成本极低,直接替换原有if逻辑即可
  • 新增筛选列只需要在filterMap里加一行配置,无需修改逻辑代码
  • 完全兼容EF/EF Core的SQL转换,没有性能问题

方案2:通用表达式树方案(支持任意实体复用)

如果需要在多个实体的筛选场景复用逻辑,可以封装通用的多列筛选扩展方法,无需针对每个实体写映射:

实现代码

public static class QueryableExtensions
{
    /// <summary>
    /// IQueryable多列模糊匹配扩展
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="query">原查询对象</param>
    /// <param name="searchValue">搜索值</param>
    /// <param name="propertyPaths">要筛选的属性路径,支持嵌套属性如"Warehouse.Description"</param>
    public static IQueryable<T> WhereMultiColumnContains<T>(this IQueryable<T> query, string searchValue, params string[] propertyPaths)
    {
        if (string.IsNullOrWhiteSpace(searchValue) || propertyPaths?.Any() != true)
            return query;

        var parameter = Expression.Parameter(typeof(T), "x");
        Expression combinedBody = null;
        var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });

        foreach (var path in propertyPaths)
        {
            // 解析嵌套属性
            Expression property = parameter;
            foreach (var propName in path.Split('.'))
            {
                property = Expression.Property(property, propName);
            }
            // 值类型转字符串
            if (property.Type != typeof(string))
            {
                var toStringMethod = property.Type.GetMethod("ToString", Type.EmptyTypes);
                property = Expression.Call(property, toStringMethod);
            }
            // 拼接Contains条件
            var containsExpr = Expression.Call(property, containsMethod, Expression.Constant(searchValue));
            combinedBody = combinedBody == null ? containsExpr : Expression.OrElse(combinedBody, containsExpr);
        }

        var lambda = Expression.Lambda<Func<T, bool>>(combinedBody, parameter);
        return query.Where(lambda);
    }
}

使用示例

if (!string.IsNullOrWhiteSpace(orderFilter.SearchValue))
{
    // 把列名转成对应的属性路径即可,列名和属性名不一致可以做一层简单映射
    var propertyPaths = orderFilter.SearchColumns.Select(col => col switch
    {
        "warehouse" => "Warehouse.Description",
        "date" => "Date",
        "orderno" => "OrderNo",
        "client" => "Client.Description",
        "salesman" => "Salesman.Username",
        _ => null
    }).Where(p => p != null).ToArray();

    query = query.WhereMultiColumnContains(orderFilter.SearchValue, propertyPaths);
}

优势

  • 一次封装,所有实体的多列筛选场景都可以复用
  • 支持嵌套属性、值类型自动转字符串,适配绝大多数模糊搜索场景

注意事项

  • 如果使用EF/EF Core,避免在筛选逻辑中调用自定义方法,否则会触发客户端计算,影响性能
  • 大小写敏感问题可以统一将属性值转小写后再匹配,和你现有SearchValue转小写的逻辑对齐
  • 针对DateTime类型的格式化匹配,建议用DbFunctions类的方法代替直接ToString,兼容性更好

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 05:45:03