在许多 AND 之间构建一个具有嵌套 OR 的动态表达式

Build a dynamic expression with nested OR between many ANDS

我正在尝试动态构建如下所示的表达式。

SomeList.Where(person => person.firstname == "foo" && 
              (person.lastanme == "bar" ||person.middleName == "bar") && 
               person.age == 65) 

我正在使用一个名为 PredicateBuilder 的 class :

public static class PredicateBuilder
{
  public static Expression<Func<T, bool>> True<T> ()  { return f => true;  }
  public static Expression<Func<T, bool>> False<T> () { return f => false; }

  public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                      Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
  }

  public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                       Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
  }
}

我首先验证搜索对象的属性是否已填写,如果已填写,然后我将调用一个方法来基于该 属性 构建谓词。然后我将所有这些添加到谓词列表中。最后,我使用 PredicateBuilder.And() 方法将所有谓词附加到列表中。

但是我需要能够过滤掉两个不同的属性,如姓氏或中间名。

我想要一个示例,说明如何动态构建这些谓词并在必要时在它们之间插入一个或,或者关于更好的整体设计以满足我的需要的建议。

在下面的代码中,我调用了 CreatePredicate()。

    private  static List<Expression<Func<DirectoryEntry,bool>>> _conditionsList = new List<Expression<Func<DirectoryEntry,bool>>>();
    public static Expression<Func<DirectoryEntry, bool>> CreatePredicate(DirectorySearchData searchData)
    {
        _conditionsList.Clear();

        var predicate = PredicateBuilder.False<DirectoryEntry>();

        AddFirstNameCondition(searchData);
        AddLastNameCondition(searchData);
        AddUsernameCondition(searchData);

        predicate = BuilPredicateFromConditionList(predicate);

        return predicate;
    }

private static void AddFirstNameCondition(DirectorySearchData searchData)
    {
        if (!String.IsNullOrWhiteSpace(searchData.FirstName))
        {
            _conditionsList.Add(entry => entry.FirstName.ToLower().Contains(searchData.FirstName.ToLower()));
        }
    }

    private static void AddLastNameCondition(DirectorySearchData searchData)
    {
        if (!String.IsNullOrWhiteSpace(searchData.LastName))
        {
            _conditionsList.Add(entry => entry.LastName.ToLower().Contains(searchData.LastName.ToLower()));
        }
    }

    private static void AddUsernameCondition(DirectorySearchData searchData)
    {
        if (!String.IsNullOrWhiteSpace(searchData.UserName))
        {
            _conditionsList.Add(entry => entry.Username.ToLower().Contains(searchData.UserName.ToLower()));
        }
    }

private static Expression<Func<DirectoryEntry, bool>> BuilPredicateFromConditionList(Expression<Func<DirectoryEntry, bool>> predicate)
    {
        if (_conditionsList == null || _conditionsList.Count <= 0) return predicate;

        predicate = PredicateBuilder.True<DirectoryEntry>();
        foreach (var condition in _conditionsList)
        {
            predicate = PredicateBuilder.And(predicate, condition);
        }
        return predicate;
    }

    public class DirectorySearchData
{

    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string UserName { get; set; }
    public string CampusPhone { get; set; }
    public string CampusAddress { get; set; }
    public string CampusBox { get; set; }
    public string HomeAddress { get; set; }
    public string HomeState { get; set; }
    public string DepartmentOrOffice { get; set; }
    public string StudentMajor { get; set; }
    public string Concentration { get; set; }
    public string SgaCabinetPositionName { get; set; }
    public string Hiatus { get; set; }
    public string StudentClass { get; set; }

    public DirectorySearchData()
    {
        LastName = "";
        FirstName = "";
        UserName = "";
        CampusPhone = "";
        CampusAddress = "";
        CampusBox = "";
        HomeAddress = "";
        HomeState = "";
        DepartmentOrOffice = "";
        StudentMajor = "";
        Concentration = "";
        SgaCabinetPositionName = "";
        Hiatus = "";
        StudentClass = "";
    }
}

}

要创建更复杂的表达式,您可以在 Add*Condition 方法中使用更复杂的表达式。对于您的案例中的构建 OR 表达式,您可以使用下一个方法:

private static void AddLastNameOrMiddleNameCondition(DirectorySearchData searchData)
{
  if (!string.IsNullOrWhiteSpace(searchData.LastName))
  {
    var lastNameOrMiddleName = PredicateBuilder.Or(
      (DirectorySearchData entry) => entry.LastName.ToLower().Contains(searchData.LastName.ToLower()),
      (DirectorySearchData entry) => entry.MiddleName.ToLower().Contains(searchData.MiddleName.ToLower()));

    _conditionsList.Add(lastNameOrMiddleName);
  }
}

更新: 或更简洁的示例:

private static void AddLastNameOrMiddleNameCondition(DirectorySearchData searchData)
{
  if (!string.IsNullOrWhiteSpace(searchData.LastName))
  {
    var inner = PredicateBuilder.New<DirectorySearchData>(false);
    // BTW, Use New<T> insted of PredicateBuilder.False<DirectorySearchData>(); methods False<T> and True<T> are obsolete.
    inner = inner.Or(entry => entry.LastName.ToLower().Contains(searchData.LastName.ToLower()));
    inner = inner.Or(entry => entry.MiddleName.ToLower().Contains(searchData.MiddleName.ToLower()));

    _conditionsList.Add(inner);
  }
}