【问题标题】:How do I dynamically construct a predicate method from an expression tree?如何从表达式树动态构造谓词方法?
【发布时间】:2010-09-20 21:23:22
【问题描述】:

这是场景: Silverlight 4.0、DataGrid、PagedCollectionView itemssource。 目标是将过滤器应用于 PCV。过滤器必须是 Predicate<object>(Method) - 其中 Method 对对象实施一些逻辑并返回 true/false 以供包含。 我需要在过滤器逻辑中选择性地包含 3 个不同的标准,并且显式代码很快就会变得丑陋。我们不想这样,不是吗?

所以我看到有一种方法可以使用 PredicateBuilder 构建表达式树并将其传递到 Linq.Where,a la:

IQueryable<Product> SearchProducts (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Product>();

  foreach (string keyword in keywords)
  {
    string temp = keyword;
    predicate = predicate.Or (p => p.Description.Contains (temp));
  }
  return dataContext.Products.Where (predicate);
}

[顺便说一句,这不是我想要做的]

有 3 个可选条件,我想写如下内容:

 Ratings.Filter = BuildFilterPredicate();  // Ratings = the PagedCollectionView


private Predicate<object> BuildFilterPredicate()
{
  bool FilterOnOrder = !String.IsNullOrEmpty(sOrderNumberFilter);
  var predicate = PredicateBuilder.False<object>();
  if (ViewMineOnly)
  {
    predicate = predicate.And(Rating r => sUserNameFilter == r.Assigned_To);
  }
  if (ViewStarOnly)
  {
    predicate = predicate.And(Rating r => r.Star.HasValue && r.Star.Value > 0);
  }
  if (FilterOnOrder)
  {
    predicate = predicate.And(Rating r => r.ShipmentInvoice.StartsWith(sOrderNumberFilter));
  }
  return predicate;
}

当然这不会编译,因为 PredicateBuilder 创建一个 Expression&lt;Func&lt;T, bool&gt;&gt; 而不是实际的谓词方法。但我看到有一些方法可以将表达式树转换为方法,所以在我看来,应该有一种方法可以完成我所追求的目标,而无需求助于一堆嵌套的 if/then/else 语句。

所以问题是 - 有没有办法动态构建谓词方法?

TIA

【问题讨论】:

    标签: silverlight filter expression-trees predicatebuilder


    【解决方案1】:

    要为 PagedCollectionView 执行此操作,您需要有一个谓词。所以它看起来像:

    private Predicate<object> ConvertExpressionToPredicate(Expression<Func<object, bool>> exp)
    {
      Func<object, bool> func = exp.Compile();
      Predicate<object> predicate = new Predicate<object>(func);
      //Predicate<object> predicate = t => func(t);     // also works
      //Predicate<object> predicate = func.Invoke;      // also works
      return predicate;
    }
    

    并构建表达式:

    private Expression<Func<object, bool>> BuildFilterExpression()
    {
      ...snip...
      var predicate = PredicateBuilder.True<object>();
      if (ViewMineOnly)
      {
        predicate = predicate.And(r => ((Rating)r).Assigned_To.Trim().ToUpper() == sUserNameFilter || ((Rating)r).Assigned_To.Trim().ToUpper() == "UNCLAIMED");
      }
      if (ViewStarOnly)
      {
        predicate = predicate.And(r => ((Rating)r).Star.HasValue && ((Rating)r).Star.Value > 0);
      }
      if (FilterOnOrder)
      {
        predicate = predicate.And(r => ((Rating)r).ShipmentInvoice.Trim().ToUpper().StartsWith(sOrderNumberFilter));
      }
      if (ViewDueOnly)
      {
        predicate = predicate.And(r => ((Rating)r).SettlementDueDate <= ThisThursday);
      }
      return predicate;
    }
    

    然后设置过滤器:

    Ratings.Filter = ConvertExpressionToPredicate(BuildFilterExpression());
    

    【讨论】:

      【解决方案2】:

      我遇到了同样的问题。我有 3 个标准。 我所做的如下:

      • 一种验证每个标准的方法
      • 一种验证对象的方法

      代码看起来很干净,易于维护。

      Ratings.Filter = new predicate<objects>(validateObject);
      
      private bool validateObject(object o)
      {
        return validateFirstCriteria(o) && 
               validateSecondCriteria(o) && 
               validateThirdCriteria(o);
      }
      
      private bool validateFirstObject(object o)
      {
        if (ViewMineOnly)
        {
          Rating r = o as Rating;
          if (o != null)
          {
            return  (r.Star.HasValue && r.Star.Value > 0);
          }
        }
        return false;
      }
      private bool validateSecondObject(object o)
      {
        if (ViewStarOnly)
        {
          Rating r = o as Rating;
          if (o != null)
          {
            return sUserNameFilter == r.Assigned_To;
          }
        }
        return false;
      }
      private bool validateThirdObject(object o)
      {
        if (FilterOnOrder)
        {
          Rating r = o as Rating;
          if (o != null)
          {
            return r.ShipmentInvoice.StartsWith(sOrderNumberFilter);
          }
        }
        return false;
      }
      

      编辑

      如果你想坚持表达式树。你可以看看这里:http://msdn.microsoft.com/en-us/library/bb882536.aspx

      您可以将表达式树转换为 lambda 表达式,然后编译 lambda 表达式。之后,您可以将其用作方法。例子:

              // The expression tree to execute.
              BinaryExpression be = Expression.Power(Expression.Constant(2D), Expression.Constant(3D));
      
              // Create a lambda expression.
              Expression<Func<double>> le = Expression.Lambda<Func<double>>(be);
      
              // Compile the lambda expression.
              Func<double> compiledExpression = le.Compile();
      
              // Execute the lambda expression.
              double result = compiledExpression();
      
              // Display the result.
              Console.WriteLine(result);
      
              // This code produces the following output:
              // 8
      

      【讨论】:

      • 谢谢,这确实清理了代码,但我仍然希望找到基于表达式的解决方案。
      • 编辑了一个可以帮助你的链接。
      【解决方案3】:

      感谢本杰明的提示和这篇文章 -> How to convert Func<T, bool> to Predicate<T>? 我想到了。
      本质是:


      private static Predicate<T> ConvertExpressionToPredicate(Expression<Func<T, bool>> exp)
      {
        Func<T, bool> func = exp.Compile();
        Predicate<T> predicate = new Predicate<T>(func);
        //Predicate<T> predicate = t => func(t);     // also works
        //Predicate<T> predicate = func.Invoke;      // also works
        return predicate;
      }
      

      这会将表达式树编译为单个函数并返回一个谓词来调用该函数。
      在使用中,它看起来像:

      private static bool ViewStarOnly;
      private static bool LongNameOnly;
      static void Main(string[] args)
      {
        List<Dabble> data = GetSomeStuff();
        ViewStarOnly = true;
        LongNameOnly = true;
        Expression<Func<Dabble, bool>> exp = BuildFilterExpression();
        List<Dabble> filtered = data.FindAll(ConvertExpressionToPredicate(exp));
        PrintSomeStuff(filtered);
      }
      
      private static Predicate<Dabble> ConvertExpressionToPredicate(Expression<Func<Dabble, bool>> exp)
      {
        Func<Dabble, bool> func = exp.Compile();
        Predicate<Dabble> predicate = new Predicate<Dabble>(func);
        //Predicate<Dabble> predicate = t => func(t);     // also works
        //Predicate<Dabble> predicate = func.Invoke;      // also works
        return predicate;
      }
      
      private static Expression<Func<Dabble, bool>> BuildFilterExpression()
      {
        var predicate = PredicateBuilder.True<Dabble>();
        if (ViewStarOnly)
        {
          predicate = predicate.And(r => r.Star.HasValue && r.Star.Value > 0);
        }
        if (LongNameOnly)
        {
          predicate = predicate.And(r => r.Name.Length > 3);
        }
        return predicate;
      }
      

      谢谢!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多