¿Cómo construyo dinámicamente un método de predicado a partir de un árbol de expresión?

expression-trees filter predicatebuilder silverlight

Pregunta

Aquí está el escenario: Silverlight 4.0, DataGrid, PagedCollectionView itemssource. El objetivo es aplicar un filtro a la PCV. El filtro debe ser un Predicate<object>(Method) , donde el Método implementa cierta lógica contra el objeto y devuelve verdadero / falso para su inclusión. Lo que tengo es la necesidad de incluir opcionalmente 3 criterios diferentes en la lógica del filtro y el código explícito rápidamente se pone feo. No queremos eso, ¿verdad?

Entonces veo que hay una manera de construir un árbol de expresiones usando PredicateBuilder y pasar eso a 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);
}

[esto no es lo que estoy tratando de hacer por cierto]

Con 3 criterios opcionales, quiero escribir algo como:

 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;
}

Por supuesto, esto no se compilará porque PredicateBuilder crea una Expression<Func<T, bool>> no un método de predicado real. Pero veo que hay maneras de convertir un árbol de expresiones en un método, por lo que me parece que debería haber una manera de lograr lo que estoy buscando sin tener que recurrir a un montón de declaraciones anidadas if / then / else.

Entonces la pregunta es: ¿hay una manera de construir un método de predicado dinámicamente?

TIA

Respuesta popular

para hacer esto para un PagedCollectionView, necesita tener un Predicado. Así se ve:

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;
}

y construir la expresión:

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;
}

y luego configurar el filtro:

Ratings.Filter = ConvertExpressionToPredicate(BuildFilterExpression());


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow