I'm new to expression building, so bear with me a little bit.
I'm building off of this answer, using PredicateBuilder
for building a predicate with an unknown number of filters. Say I have these classes:
public FilterTerm
{
public string ComparisonOperatorA { get; set; }
public decimal ValueA { get; set; }
public string ComparisonOperatorB { get; set; }
public decimal ValueB { get; set; }
public string ComparisonOperatorC { get; set; }
public decimal ValueC { get; set; }
}
public Item
{
public int Id { get; set; }
public string Name { get; set; }
public decimal SkillLevelA { get; set; }
public decimal SkillLevelB { get; set; }
public decimal SkillLevelC { get; set; }
}
where FilterTerm.ComparisonOperator[A|B|C]
can be any of >, >=, <, <=, ==
, which a user submits N FilterTerm
s to filter results from the db (via EF context) Item
s. My main query is set up this way:
[...]
List<FilterTerm> filterTerms = GetFilterTerms();
var itemsQuery = context.GetAll<Item>(); // reutrns IQueryable<Item>
itemsQuery = itemsQuery.ApplyFilters(filterTerms);
var items = await itemsQuery.ToListAsync();
[...]
I'm building the base predicate like this, with missing knowledge (I'm using assigned variables in the loop so values aren't evaluated at execution time):
private IQueryable<Item> ApplyFilters(IQueryable<Item> query, FilterTerms filterTerms)
{
Expression<Func<Item, bool>> superPredicate = PredicateBuilder.False<Item>()
foreach (var filterTerm in filterTerms)
{
Expression<Func<Item, bool>> subPredicate = PredicateBuilder.True<Item>();
subPredicate = subPredicate.And<Item>(GetDynamicPredicate(i => i.SkillLevelA, filterTerm.ValueA, filterTerm.ComparisonOperatorA));
subPredicate = subPredicate.And<Item>(GetDynamicPredicate(i => i.SkillLevelB, filterTerm.ValueB, filterTerm.ComparisonOperatorB));
subPredicate = subPredicate.And<Item>(GetDynamicPredicate(i => i.SkillLevelC, filterTerm.ValueC, filterTerm.ComparisonOperatorC));
superPredicate = superPredicate.Or(subPredicate);
}
return query.Where(superPredicate);
}
private Expression<Func<Item, bool>> GetDynamicPredicate(XXXX xxxx, decimal value, string comparisonOperator)
{
Expression<Func<Item, bool>> predicate;
switch (comparisonOperator)
{
case "<":
predicate = Expression.LessThan(xxxx, value);
break;
[...]
}
return predicate;
}
where I don't know what to have for XXXX xxxx
, which I prefer to have as a term selector like i => i.[Property]
(is it possible to do this in a way that maintains Query building before sending it to the DB for execution?), and I know that the Expression.LessThan
requires Expression left, Expression right
for the signature, but I'm stuck with how to complete this.
I've seen questions like this one, but I just I can't seem to figure out how to apply the answers to my use case. Can anyone help provide the missing knowledge? It's been a few hours of trial-and-error, and I may just need something spoon-fed to me to connect the dots... I think I'm missing some steps with BinaryExpression
and Lambda
building, but I just can't piece it together.