I have a db table that stores the following:
RuleID objectProperty ComparisonOperator TargetValue
1 age \'greater_than\' 15
2
I added implementation for and,or between rules i added class RuleExpression that represent the root of a tree that can be leaf the is simple rule or can be and,or binary expressions there for they dont have rule and have expressions:
public class RuleExpression
{
public NodeOperator NodeOperator { get; set; }
public List Expressions { get; set; }
public Rule Rule { get; set; }
public RuleExpression()
{
}
public RuleExpression(Rule rule)
{
NodeOperator = NodeOperator.Leaf;
Rule = rule;
}
public RuleExpression(NodeOperator nodeOperator, List expressions, Rule rule)
{
this.NodeOperator = nodeOperator;
this.Expressions = expressions;
this.Rule = rule;
}
}
public enum NodeOperator
{
And,
Or,
Leaf
}
I have another class that compile the ruleExpression to one Func
public static Func CompileRuleExpression(RuleExpression ruleExpression)
{
//Input parameter
var genericType = Expression.Parameter(typeof(T));
var binaryExpression = RuleExpressionToOneExpression(ruleExpression, genericType);
var lambdaFunc = Expression.Lambda>(binaryExpression, genericType);
return lambdaFunc.Compile();
}
private static Expression RuleExpressionToOneExpression(RuleExpression ruleExpression, ParameterExpression genericType)
{
if (ruleExpression == null)
{
throw new ArgumentNullException();
}
Expression finalExpression;
//check if node is leaf
if (ruleExpression.NodeOperator == NodeOperator.Leaf)
{
return RuleToExpression(ruleExpression.Rule, genericType);
}
//check if node is NodeOperator.And
if (ruleExpression.NodeOperator.Equals(NodeOperator.And))
{
finalExpression = Expression.Constant(true);
ruleExpression.Expressions.ForEach(expression =>
{
finalExpression = Expression.AndAlso(finalExpression, expression.NodeOperator.Equals(NodeOperator.Leaf) ?
RuleToExpression(expression.Rule, genericType) :
RuleExpressionToOneExpression(expression, genericType));
});
return finalExpression;
}
//check if node is NodeOperator.Or
else
{
finalExpression = Expression.Constant(false);
ruleExpression.Expressions.ForEach(expression =>
{
finalExpression = Expression.Or(finalExpression, expression.NodeOperator.Equals(NodeOperator.Leaf) ?
RuleToExpression(expression.Rule, genericType) :
RuleExpressionToOneExpression(expression, genericType));
});
return finalExpression;
}
}
public static BinaryExpression RuleToExpression(Rule rule, ParameterExpression genericType)
{
try
{
Expression value = null;
//Get Comparison property
var key = Expression.Property(genericType, rule.ComparisonPredicate);
Type propertyType = typeof(T).GetProperty(rule.ComparisonPredicate).PropertyType;
//convert case is it DateTimeOffset property
if (propertyType == typeof(DateTimeOffset))
{
var converter = TypeDescriptor.GetConverter(propertyType);
value = Expression.Constant((DateTimeOffset)converter.ConvertFromString(rule.ComparisonValue));
}
else
{
value = Expression.Constant(Convert.ChangeType(rule.ComparisonValue, propertyType));
}
BinaryExpression binaryExpression = Expression.MakeBinary(rule.ComparisonOperator, key, value);
return binaryExpression;
}
catch (FormatException)
{
throw new Exception("Exception in RuleToExpression trying to convert rule Comparison Value");
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}