How to implement a rule engine?

后端 未结 10 1948
借酒劲吻你
借酒劲吻你 2020-11-27 23:58

I have a db table that stores the following:

RuleID  objectProperty ComparisonOperator  TargetValue
1       age            \'greater_than\'             15
2          


        
10条回答
  •  栀梦
    栀梦 (楼主)
    2020-11-28 00:24

    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);
            }
    
        }
    

提交回复
热议问题