2

I was looking at a simple rule engine http://netmatze.wordpress.com/2012/01/22/building-a-rule-engine-in-c/ and I'm doing something very similar to this. I have two classes that look like:

    class A
    {
      public List<B> ListB { get; set; }
    }

    Class B
    {
      public int ID { get; set; }
    }

With my rule set looking like:

    List<Rule> rules = new List<Rule>{
      new Rule("listB", ExpressionType.Loop, 1, "ID")
    };

I'm trying to build the expression to basically look at class A property listB, loop it camparing each item's ID property to see if at least one equals 1. I'm having trouble on how to do this. I currently have something like (I have hard coded values set in this, but it will eventually be changed to be generic as much as possible). This expression does not work, I get compile exceptions:

    var parameterExpression = Expression.Parameter(typeof(A));
    var listB = MemberExpression.Property(parameterExpression, "ListB");
    var leftOperand = MemberExpression.Property(Expression.Parameter(typeof(B)), "ID");
    var rightOperand = Expression.Constant(1); //1
    var found = Expression.Variable(typeof(bool), "found");

    return Expression.Lambda<Func<T, bool>>(
          Expression.Block(
              listB,
              found,
              Expression.Loop( 
                Expression.Block(
                  Expression.IfThen(
                    Expression.Equal(
                      leftOperand,
                      rightOperand
                     ),//equal
                     Expression.Assign(
                       found,
                       Expression.Constant(true)
                     )//set to true
                  )                     
                )//block
              )//loop
            ),
            A
      ).Compile();

I'll end up calling the rule set against my object like so:

    Engine ruleEngine = new Engine();
    var compiledRules = rules.Select(r => ruleEngine.CompileRule<A>(r)).ToList();
    var result = compiledRules.All(rule => rule(objA));

My questions are:

  1. How do I get this function to return true/false if any of the list items matched the condition.
  2. How do you prevent the Expression.Loop to stop looping once all list items are compared (and none of them matched)?

Thanks for any help.

dcastro
  • 66,540
  • 21
  • 145
  • 155
riververy
  • 21
  • 1

2 Answers2

1

Why use a loop? You wouldn't use a loop if you were coding the check in C#. You'd use Enumerable.Any. So generate the following expression:

A a;
return a.ListB.Any(b => b.ID == 1);

This is translated to:

A a;
return Enumerable.Any(a.ListB, b => b.ID == 1);

This is easily translatable to expression trees.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Pretty sure I can't use a linq statement like that (even though I would love to). The method this code is in does not know about ListB. It only knows property names for class A and B and the class A which is passed over via a generic T. Method this code is in looks like this: public Func BuildExpression(string propertyName, ExpressionType ruleOperator, object value, ParameterExpression parameterExpression, string innerPropertyName) { – riververy Mar 27 '14 at 20:35
  • Anytime you write a database query in C# you are implicitly creating such an expression tree. It is certainly possible. Note, that extension methods are equivalent to static method invocations (see the edit).; I understand that none of the identifier names are known statically. I just tried to give an example of the structure you have to build with expression trees. Most C# expressions are exactly translatable to expression trees. For example, the `Enumerable.Any` is just an `Expression.Call`. Does that help? – usr Mar 27 '14 at 21:56
0

Following your last comment it sounds like you could use the approach i suggested for another question. Replace this part:

var childProperty = parameter.Type.GetProperty(properties[0]);
var left = Expression.Property(parameter, childProperty);
var right = Expression.Constant(test, typeof(int));
navigationPropertyPredicate = Expression.Equal(left, right);
resultExpression = MakeLambda(parameter, navigationPropertyPredicate);

with something using your ruleOperator and value

Community
  • 1
  • 1
user3411327
  • 1,031
  • 8
  • 14