1

I'm attempting to build out some additional feature for a rules engine that I'm working on using Expression Trees which is based on this post - How to implement a rule engine?

One of the rules I have is to add minutes to a property if that properties type is found to be a DateTime. So for example I could have "DOB + 60" or "DOB - 180".

My current code is working for this:

 var expression = new string[] {"DOB", "+", "60"};

 var property = Expression.Property(param, expression[0]);
 var method = typeof(T).GetProperty(expression[0]).PropertyType.GetMethod("AddMinutes");

 /* If we have a AddMinutes method then assume type is of DateTime */
 if (method != null)
 {
     var tParam = method.GetParameters()[0].ParameterType;

     /* Calculate if we're adding or subtracting minutes */
     var convertedNo = expression[1] == "-" ? int.Parse(expression[2]) * -1 : int.Parse(expression[2]);

      var number = Expression.Constant(Convert.ChangeType(convertedNo, tParam));
      var exp = Expression.Call(property, method, number);

      /* Allows for left property < DOB.AddMinutes(60) */
      return Expression.MakeBinary(ExpressionType.LessThan, left, exp);
}

The trouble I'm having is that the DOB property may be set to DateTime.Min so that when my expression comes to evaluate "DOB - 180" in that case an Out of Bounds exception is thrown as it's attempting to subtract 180 mins from DateTime.Min.

I attempted to get round this issue by using an Expression.IfThenElse so that if the DateTime property is equal to DateTime.Min, don't do the calculation and just evaluate on DateTime.Min.

My updated code looks like this:

 var expression = new string[] {"DOB", "-", "180"};

 var property = Expression.Property(param, expression[0]);
 var method = typeof(T).GetProperty(expression[0]).PropertyType.GetMethod("AddMinutes");

 /* If we have a AddMinutes method then assume type is of DateTime */
 if (method != null)
 {
     var tParam = method.GetParameters()[0].ParameterType;

     /* Calculate if we're adding or subtracting minutes */
     var convertedNo = expression[1] == "-" ? int.Parse(expression[2]) * -1 : int.Parse(expression[2]);

      var number = Expression.Constant(Convert.ChangeType(convertedNo, tParam));
      var exp = Expression.Call(property, method, number);

      var minDate = Expression.Constant(Convert.ChangeType(DateTime.MinValue, typeof(DateTime)));
      var minDateCheck = Expression.MakeBinary(ExpressionType.Equal, property, minDate);

      var minDateExp = Expression.IfThenElse(minDateCheck, minDate, exp);

      /* To allow for left property < DOB == DateTime.Min ? DateTime.Min : DOB.AddMinutes(60) */
      return Expression.MakeBinary(ExpressionType.LessThan, left, minDateExp); // Exception thrown here
}   

Unfortunately this throws the exception - Cannot compare DateTime with return type of void. I've tried several variations but always get the same exception.

Would anyone be able to show me how I could construct an expression that would be able to do this logic?

Community
  • 1
  • 1
QTheIntro
  • 55
  • 10
  • Situations like this I do is in a test program do `Expression> test = (x) => x < DOB == DateTime.Min ? DateTime.Min : DOB.AddMinutes(60)` then look at `test` in the debugger to see how the compiler generated it. – Scott Chamberlain Jul 20 '16 at 20:15
  • Funnily enough, it was one of my tests that caught the issue!. Thanks for the tip, I'll give this a try in the future. – QTheIntro Jul 21 '16 at 07:58

1 Answers1

3

Expression.IfThenElse is equivalent of the C# - well, if statement, while what you need is the expression equivalent of the C# ?: (a.k.a conditional) operator, which is ConditionalExpression and is produced by Expression.Condition method.

Shortly, replace

 var minDateExp = Expression.IfThenElse(minDateCheck, minDate, exp);

with

 var minDateExp = Expression.Condition(minDateCheck, minDate, exp);

and you are done.

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Perfect, worked a treat - I was hoping it was something simple that I'd missed. I appreciate the help and the explanation - it will be good to know for other expressions I'm going to build out. Thanks again. – QTheIntro Jul 21 '16 at 07:55