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

如何简化条件构建Where查询条件时的重复校验逻辑?

优化方案

针对你遇到的重复校验参数、容易漏写的问题,这里有几种简洁且不易出错的实现方式:

方案一:提前判断无过滤条件的情况

直接先检查所有参数是否为空,是的话直接返回原集合,避免后续构建表达式再置空的操作:

static IQueryable<Product> Filter(this IQueryable<Product> products,
    int? id = null,
    string? name = null,
    decimal? maxPrice = null)
{
    // 提前处理无过滤条件的场景,直接返回原集合
    if (id is null && name is null && maxPrice is null)
        return products;

    // 仅当有过滤条件时构建表达式
    Expression<Func<Product, bool>> criteria = p =>
        (id == null ? true : p.Id == id) &&
        (name == null ? true : p.Name.ToLower().Contains(name.ToLower())) &&
        (maxPrice == null ? true : p.Price <= maxPrice);

    return products.Where(criteria);
}

这个方案逻辑清晰,虽然还是要判断所有参数,但避免了后续将criteria置空的冗余操作,代码更简洁。

方案二:抽取过滤状态变量

把“是否有过滤条件”的判断抽成一个变量,避免重复写参数校验:

static IQueryable<Product> Filter(this IQueryable<Product> products,
    int? id = null,
    string? name = null,
    decimal? maxPrice = null)
{
    // 只要有一个参数不为空,就说明需要过滤
    bool hasFilters = id is not null || name is not null || maxPrice is not null;

    if (!hasFilters)
        return products;

    Expression<Func<Product, bool>> criteria = p =>
        (id == null ? true : p.Id == id) &&
        (name == null ? true : p.Name.ToLower().Contains(name.ToLower())) &&
        (maxPrice == null ? true : p.Price <= maxPrice);

    return products.Where(criteria);
}

这种写法比方案一更易维护——后续新增参数时,只需要在hasFilters的判断里加一个|| 新参数 is not null即可,比原来的多个&&更不容易漏写。

方案三:动态构建表达式树

通过手动拼接表达式树,只在参数不为空时添加对应的过滤条件,彻底避免重复校验的问题:

static IQueryable<Product> Filter(this IQueryable<Product> products,
    int? id = null,
    string? name = null,
    decimal? maxPrice = null)
{
    var parameter = Expression.Parameter(typeof(Product), "p");
    Expression? filterBody = null;

    // 处理Id过滤条件
    if (id is not null)
    {
        var idEqualExpr = Expression.Equal(
            Expression.Property(parameter, nameof(Product.Id)),
            Expression.Constant(id));
        filterBody = idEqualExpr;
    }

    // 处理Name过滤条件
    if (name is not null)
    {
        var productNameLower = Expression.Call(
            Expression.Property(parameter, nameof(Product.Name)),
            typeof(string).GetMethod(nameof(string.ToLower), Type.EmptyTypes)!);
        var inputNameLower = Expression.Constant(name.ToLower());
        var containsExpr = Expression.Call(
            productNameLower,
            typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string)})!,
            inputNameLower);

        filterBody = filterBody == null ? containsExpr : Expression.AndAlso(filterBody, containsExpr);
    }

    // 处理MaxPrice过滤条件
    if (maxPrice is not null)
    {
        var priceLessOrEqualExpr = Expression.LessThanOrEqual(
            Expression.Property(parameter, nameof(Product.Price)),
            Expression.Constant(maxPrice));

        filterBody = filterBody == null ? priceLessOrEqualExpr : Expression.AndAlso(filterBody, priceLessOrEqualExpr);
    }

    // 没有任何过滤条件,直接返回原集合
    if (filterBody == null)
        return products;

    var criteria = Expression.Lambda<Func<Product, bool>>(filterBody, parameter);
    return products.Where(criteria);
}

这个方案的优势在于:

  • 完全避免了重复校验参数的问题,每个参数的处理逻辑独立
  • 生成的表达式更高效,不会包含多余的true && ...逻辑
  • 后续新增过滤参数时,只需要添加对应的if块即可,不会影响其他逻辑

另外,你之前尝试的写法报错是因为表达式树不允许包含赋值操作,所以那种通过在表达式里给变量赋值的方式不可行,上面的方案都避开了这个问题。

内容的提问来源于Stack Exchange,提问作者Display Name

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 06:15:39