1

下面是场景: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<Func<T, bool>>不是实际的谓词方法。但是我看到有一些方法可以将表达式树转换为方法,所以在我看来应该有一种方法可以完成我所追求的目标,而无需求助于一堆嵌套的 if/then/else 语句。

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

TIA

4

3 回答 3

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());
于 2010-10-07T17:25:35.380 回答
0

感谢 Benjamin 的提示和这篇文章 ->如何将 Func<T, bool> 转换为 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;
}

谢谢!

于 2010-09-30T16:45:34.320 回答
0

我遇到了同样的问题。我有 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
于 2010-09-21T14:38:32.537 回答