0

I want to validate rule against input array data with runtime indexer not with some fixed zero index value. It works if i insert data one by one in session.Insert() It does't work if i use sesion.InsertAll() with array data

I tried providing Expression.Constant value to indexer but no action triggers

class Program
    {
        static void Main(string[] args)
        {
            RuleTestWithSingleInsertData();

           // RuleTestWithInsertDataAll();

            Console.ReadKey();
        }

        public static void RuleTestWithSingleInsertData()
        {
            try
            {
                CustomRuleRepository repository = new CustomRuleRepository();

                List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
                rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });

                repository.LoadRuleForTest1(rules.FirstOrDefault());

                //Compile rules
                var factory = repository.Compile();

                //Create a working session
                var session = factory.CreateSession();

                RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
                {
                    ruleList = rules,
                    customerData = new List<Customer>() { new Customer { Name = "A", Age = 19 },
                    new Customer { Name = "B", Age = 26 } }.ToArray()
                };

                 session.InsertAll(ruleEngineRequestModel.customerData);

                var IspassedorNot = session.Fire(); 
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public static void RuleTestWithInsertDataAll()
        {
            try
            {
                CustomRuleRepository repository = new CustomRuleRepository();

                List<RuleEngineEntity> rules = new List<RuleEngineEntity>();
                rules.Add(new RuleEngineEntity { FieldName = "Age", Name = "CustomerCheck", Value = 25 });

                repository.LoadRuleForTest2(rules.FirstOrDefault());

                //Compile rules
                var factory = repository.Compile();

                //Create a working session
                var session = factory.CreateSession();

                RuleEngineRequestModel ruleEngineRequestModel = new RuleEngineRequestModel
                {
                    ruleList = rules,
                    customerData = new List<Customer>() { new Customer { Name = "A", Age = 28 },
                    new Customer { Name = "B", Age = 26 } }.ToArray()
                };

                session.InsertAll(ruleEngineRequestModel.customerData);

                var IspassedorNot = session.Fire();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }

    public class RuleEngineRequestModel
    {
        public List<RuleEngineEntity> ruleList { get; set; }

        public Customer[] customerData { get; set; }

        public List<Customer> customerDataList { get; set; }
    }

    public class RuleEngineEntity
    {
        public string Name { get; set; }

        public int Value { get; set; }

        public string Operator { get; set; }

        public string FieldName { get; set; }

        public bool SendEmail { get; set; }

    }

    public class Customer
    {
        public string Name { get; set; }

        public int Age { get; set; }

    }

    public class CustomRuleRepository : IRuleRepository
    {
        private readonly IRuleSet _ruleSet = new RuleSet("customerRule");

        public IEnumerable<IRuleSet> GetRuleSets()
        {
            return new[] { _ruleSet };
        }

        public void LoadRuleForTest1(RuleEngineEntity rule)
        {
            _ruleSet.Add(BuildRuleForTest1(rule));
        }

        public void LoadRuleForTest2(RuleEngineEntity rule)
        {
            _ruleSet.Add(BuildRuleForTest2(rule));
        }

        public List<IRuleDefinition> BuildRuleForTest1(RuleEngineEntity rule)
        {
            return Test1(rule);
        }

        public List<IRuleDefinition> BuildRuleForTest2(RuleEngineEntity rule)
        {
            return Test2(rule);
        }


        public List<IRuleDefinition> Test1(RuleEngineEntity rule)
        {
            RuleBuilder builder = new RuleBuilder();
            builder.Name("DefaultRules");

            try
            {
                var modelPattern = builder.LeftHandSide().Pattern(typeof(Customer), "CustomerCheck");
                var modelParameter = modelPattern.Declaration.ToParameterExpression();

                var expres = Expression.Property(modelParameter, rule.FieldName);

                var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));

                LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
                        modelParameter);


                modelPattern.Condition(expressionCondition);

                Expression<Action<IContext, Customer, RuleEngineEntity>> action =
                                 (ctx, CustomerCheck, rules) => FireActionAsync(ctx, CustomerCheck, rules);

                builder.RightHandSide().Action(action);
            }

            catch (Exception e)
            {
                // throw new Exception(e.Message);
            }

            var buildRule = builder.Build();

            return new List<IRuleDefinition> { buildRule };
        }

        public List<IRuleDefinition> Test2(RuleEngineEntity rule)
        {
            RuleBuilder builder = new RuleBuilder();
            builder.Name("DefaultRules");

            try
            {                
                   var modelPattern = builder.LeftHandSide().Pattern(typeof(RuleEngineRequestModel), "CustomerCheck");
                var modelParameter = modelPattern.Declaration.ToParameterExpression();
                var customerDataInArray = Expression.Property(modelParameter, nameof(RuleEngineRequestModel.customerData));

                var indx = Expression.Parameter(typeof(int), "index");

                var customerData = Expression.ArrayIndex(customerDataInArray, indx);

                var expres = Expression.Property(customerData, rule.FieldName);

                var binaryExpression = Expression.GreaterThan(expres, Expression.Constant(rule.Value));

                LambdaExpression expressionCondition = Expression.Lambda(binaryExpression,
                        modelParameter);

                modelPattern.Condition(expressionCondition);

                Expression<Action<IContext, Customer>> action =
                                  (ctx, CustomerCheck) => FireActionAsync(ctx, CustomerCheck, null);

                builder.RightHandSide().Action(action);
            }

            catch (Exception e)
            {
                // throw new Exception(e.Message);
            }

            var buildRule = builder.Build();

            return new List<IRuleDefinition> { buildRule };
        }

        public void FireActionAsync(IContext ctx, Customer customer, RuleEngineEntity rule=null)
        {
            Console.WriteLine($"{rule.Name} Triggered");
        }
    }

Error: variable 'index' of type 'System.Int32' referenced from scope '', but it is not defined

Expected: want to validate rule against array data with dynamic indexer.

1 Answers1

1

At a quick glance, it appears that you are passing in an array of Customers when inserting the facts into the session, but the rule is looking for the RuleEngineRequestModel.

Also, side note - why are you initialising an array as a List, then converting to an array, rather than just initialising as an array? i.e.

var ruleEngineRequestModel = new RuleEngineRequestModel
{
    ruleList = rules,
    customerData = {
        new Customer { Name = "A", Age = 28 },
        new Customer { Name = "B", Age = 26 }
    }
};

Finally, why are you inserting the rules at the same time as the data? That seems like it would cause more headaches than benefits, especially seeing as your runtime rule builder ignores them entirely.

EDIT: Having had a chance to see the updated code, this confirms my suspicions - if you use Rule 1 for both tests, it should work (rather, it will fire for each individual customer). The reason why Rule 2 never works is because it's expecting a RuleEngineRequestModel, but you are passing the IEnumerable in directly. Either pass in the request model directly and continue to use Rule 2, or scrap Rule 2 entirely.

  • I was also testing with list data. No rule is inserted at different step and data is inserting separately at session.InsertAll(ruleEngineRequestModel.customerData). – Ghanshyam Singh Aug 30 '19 at 08:47
  • U got any luck with array indexer ? – Ghanshyam Singh Aug 30 '19 at 08:50
  • You seem to have glossed over my original point - that rule builder should never operate because the model pattern is expecting to see an object of type RuleEngineRequestModel, but you are instead passing in the customer array. What happens if you pass in the request model itself? You have also created variables named indx and IndexExpression, but they don't appear to be used anywhere. – Nicholas Tyrrell Aug 30 '19 at 08:57
  • This is all for customer array data: I have updated the use of IndexExpression although i tried it before. Yes pattern expecting object of type RuleEngineRequestModel to pass conditions and operators dynamically and this is what i m passing here and to extract data property from this model i used Expression.Property(modelParameter, nameof(RuleEngineRequestModel.customerData)) . – Ghanshyam Singh Aug 30 '19 at 10:37
  • If u have any other way of writing this, can u modify my code and make it runnable ? – Ghanshyam Singh Aug 30 '19 at 10:40
  • What happens if you try `session.Insert(ruleEngineRequestModel);`? – Nicholas Tyrrell Aug 30 '19 at 10:57
  • You either need to pass in your rule engine request model, or you need to change your rule to `builder.LeftHandSide().Pattern(typeof(Customer),"CustomerCheck");`. – Nicholas Tyrrell Aug 30 '19 at 10:59
  • @GhanshyamSingh check my updated response, having seen the full data - You are passing the wrong value into your session. – Nicholas Tyrrell Aug 30 '19 at 14:12
  • well well well its working, i was doing that already but never passed array with it. – Ghanshyam Singh Aug 30 '19 at 14:25
  • Now the issue is i want to send rule and data both into another method when action triggers, i have updated the code accordingly – Ghanshyam Singh Aug 30 '19 at 14:27