1

I am trying to dynamically build predicates, from an xml string which is formatted as the following as an example:

<Group Operation="AND">
    <Group Operation="AND">
      <Group Operation="OR">
        <Filter> (ContainsValue) </Filter>
        <Filter> (StartsWithValue) </Filter>
      </Group>
      <Filter> (HasData) </Filter>
    </Group>
    <Filter> (SomeotherCondition) </Filter>
</Group>

I am sending this xml to a recursive function that traverses the xml and builds a predicate as below:

    public Stack<FilterExpressionModel> RecursiveExpressionBuilder(XElement root, Stack<FilterExpressionModel> stackelement)
    {
        Stack<Expression<Func<Product, bool>>> stackexpression = new Stack<Expression<Func<Product, bool>>>();
        var left = root.Elements().FirstOrDefault();
        var right = root.Elements().Count() == 1 ? null : root.Elements().LastOrDefault();
        if (left != null)
        {
            stackelement = RecursiveExpressionBuilder(left, stackelement);
        }
        if (right != null)
        {
            stackelement = RecursiveExpressionBuilder(right, stackelement);
        }
        if (root.Name == FilterQueryNodes.Filter)
        {
            stackelement.Push(new FilterExpressionModel() { FilterNode = root });
        }
        else if (root.Name == FilterQueryNodes.Group)
        {
            var operand2Candidate = stackelement.Pop();
            var operand1Candidate = stackelement.Pop();
            //following grabs the actual expression for the corresponding value of the xml node for example (d=>d.Value.Contains(filterValue)) for Contains
            //if the node is "filter" it grabs the filter if not, it grabs the predicate that has been built so far
            var operand1 = operand1Candidate.FilterNode != null ? FilterExpression(GetFilterRule(Convert.ToInt32(operand1Candidate.FilterNode.Value))) : operand1Candidate.PredicateBuilding;
            var operand2 = operand2Candidate.FilterNode != null ? FilterExpression(GetFilterRule(Convert.ToInt32(operand2Candidate.FilterNode.Value))) : operand2Candidate.PredicateBuilding;
            string operation = root.Attribute(FilterQueryNodes.Operation).Value;

            var predicate = BuildPredicate(operand1, operand2, operation);
            stackelement.Push(new FilterExpressionModel() { PredicateBuilding = predicate });
        }
        return stackelement;
    }

    public Expression<Func<Product, bool>> BuildPredicate(Expression<Func<Product, bool>> operand1, Expression<Func<Product, bool>> operand2, string operation)
    {
        switch (operation)
        {
            case "AND":
                operand1 = operand1.And(operand2);
                return operand1;
            case "OR":
                operand1 = operand1.Or(operand2);
                return operand1;
            default:
                return (d => true);
        }
    }
//this class is to hold the grouped predicate and the filter so that I will only have one stack
    public class FilterExpressionModel
    {
        public FilterExpressionModel() {

        }
        public Expression<Func<FieldValues, bool>> PredicateBuilding { get; set; }
        public XElement FilterNode { get; set; } 
    }

So my main problem is, this is not working when there is a depth involved as in the given example. If the xml input is something like the following, it works absolutely fine:

<Root>
    <Group Operation="AND">
      <Filter>23</Filter>
      <Filter>22</Filter>
    </Group>
</Root>

But when I have a more complicated input as the one I gave at the beginning, when I apply the predicate, It returns nothing when in fact it needs to:

products.Where(builtpredicate).ToList();

What I am asking is:

  • Am I trying to achieve something that is not plausible, with the nested predicates?
  • If it is plausible, what am I doing wrong? I read the Albahari and according to the nested predicate section there I don't see how/where mine is wrong.

This is the first time I am using predicates like this, I may be doing something very obvious terribly wrong so sorry if that is the case, but If you have any idea on how I might achieve what I am trying to, I would appreciate the help. Thanks.

Naz Ekin
  • 347
  • 6
  • 17

0 Answers0