如何简化条件构建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
相关产品推荐
相关产品推荐

