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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 22:50:26