如何在C#中动态构建多条件AND组合的Predicate进行列表查询?
动态构建Person查询Predicate的实现方案
问题背景
现有如下C#代码定义了Person类及persons列表,需要在Search方法中根据传入的Person对象属性动态构建AND连接的查询条件:
- 若
FirstName非空非空串,则加入“匹配FirstName”的条件 - 若
LastName非空非空串,则加入“匹配LastName”的条件 - 若
Age有值,则加入“匹配Age”的条件
原始代码:
public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public int? Age { get; set; } } var persons = new List<Person> { new Person { Id = 1, FirstName = "Fox", LastName = "Mulder", Age = 40 }, new Person { Id = 2, FirstName = "Dana", LastName = "Scully", Age = 35 } }; List<Person> Search(List<Person> persons, Person person) { // 需在此处动态构建Predicate,条件间为AND关系 return persons.Where(myPredicateHere); }
实现方案
方案1:链式拼接Where条件(最直观)
直接基于原列表逐步追加过滤条件,逻辑清晰,适合简单场景:
List<Person> Search(List<Person> persons, Person person) { var result = persons.AsEnumerable(); if (!string.IsNullOrEmpty(person.FirstName)) { result = result.Where(p => p.FirstName == person.FirstName); } if (!string.IsNullOrEmpty(person.LastName)) { result = result.Where(p => p.LastName == person.LastName); } if (person.Age.HasValue) { result = result.Where(p => p.Age == person.Age.Value); } return result.ToList(); }
说明:每次判断属性是否有效,若有效则追加对应的Where过滤,最终返回匹配结果。这种方式无需额外依赖,可读性极强。
方案2:使用PredicateBuilder(适合复杂场景)
如果后续需要扩展更复杂的条件组合(如OR、嵌套逻辑),可以用PredicateBuilder构建表达式树:
首先实现一个简易的PredicateBuilder工具类:
public static class PredicateBuilder { // 初始条件:恒为真 public static Expression<Func<T, bool>> True<T>() => _ => true; // 初始条件:恒为假 public static Expression<Func<T, bool>> False<T>() => _ => false; // 组合两个AND条件 public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right) { var combinedBody = Expression.AndAlso( left.Body, Expression.Invoke(right, left.Parameters.Single()) ); return Expression.Lambda<Func<T, bool>>(combinedBody, left.Parameters); } }
然后在Search方法中使用:
List<Person> Search(List<Person> persons, Person person) { var predicate = PredicateBuilder.True<Person>(); if (!string.IsNullOrEmpty(person.FirstName)) { predicate = predicate.And(p => p.FirstName == person.FirstName); } if (!string.IsNullOrEmpty(person.LastName)) { predicate = predicate.And(p => p.LastName == person.LastName); } if (person.Age.HasValue) { predicate = predicate.And(p => p.Age == person.Age.Value); } return persons.Where(predicate.Compile()).ToList(); }
说明:以“恒为真”为初始条件,逐个AND上有效属性的匹配规则,最后编译成委托传给Where。这种方式适合复杂动态查询场景,扩展性更强。
方案3:直接拼接Func委托(内存集合专用)
如果仅针对内存中的List集合查询,也可以直接拼接Func<Person, bool>委托:
List<Person> Search(List<Person> persons, Person person) { Func<Person, bool> predicate = _ => true; if (!string.IsNullOrEmpty(person.FirstName)) { // 捕获局部变量避免闭包陷阱 var targetFirstName = person.FirstName; predicate = p => predicate(p) && p.FirstName == targetFirstName; } if (!string.IsNullOrEmpty(person.LastName)) { var targetLastName = person.LastName; predicate = p => predicate(p) && p.LastName == targetLastName; } if (person.Age.HasValue) { var targetAge = person.Age.Value; predicate = p => predicate(p) && p.Age == targetAge; } return persons.Where(predicate).ToList(); }
注意:必须将目标属性值存入局部变量再捕获,避免闭包引用导致的变量值异常问题。
内容的提问来源于stack exchange,提问作者TheBoubou
相关产品推荐
相关产品推荐

