NRules Running a Compiled Rule Set of Tagged Rules

129 views
Skip to first unread message

Leon Small

unread,
Jul 12, 2019, 10:14:26 AM7/12/19
to NRules Users
I've created a TEST API method to run a Tagged set of rules and I've run into a few problems.
These rules will validate the Arrival Date and Departure Date of a Hotel Resort match a specified Weekday.
The Hotel Resort will be in a Club -> Resort Hierarchy.

I've created two "Domain Models" for the Arrival and Departure:

    public class ArrivalRuleModel
   
{
       
public int ClubID { get; set; }
       
public string ClubName { get; set; }
       
public int ResortID { get; set; }
       
public string ResortName { get; set; }    
       
public DayOfWeek ArrivalDayOfWeek { get; set; }
   
}


   
public class DepartureRuleModel
   
{
       
public int ClubID { get; set; }
       
public string ClubName { get; set; }
       
public int ResortID { get; set; }
       
public string ResortName { get; set; }    
       
public DayOfWeek DepartureDayOfWeek { get; set; }
   
}

   
I've created, named and tagged two Rules for the Arrival and Departure:

    [Name("ArrivalWeekdayRule"), Description("Rule that Validates Arrival Day Matching Specified Weekday")]
   
[Tag("Arrival"), Tag("CREATE"), Tag("EDIT")]
   
[Priority(10)]
   
public class ArrivalRule: Rule
   
{
       
public override void Define()
       
{                        
           
RuleItem ruleItem = null;
           
ArrivalRuleModel arrivalRuleModel = null;


           
// RulesInfo rulesInfo = new RulesInfo(true, "Arrival Weekday Rule");

           
When()
               
.Match<ArrivalRuleModel>(() => arrivalRuleModel)
               
.Match<RuleItem>(() => ruleItem, r => r.ClubID == arrivalRuleModel.ClubID, r => r.ResortID == arrivalRuleModel.ResortID, r => r.ValueField == arrivalRuleModel.ArrivalDayOfWeek.ToString());
           
Then()
               
.Do(ctx => ctx.Insert(new RulesInfo(true, "Arrival Weekday Rule")))
               
.Do(ctx => Console.WriteLine("Arrival Weekday Rule Ran: {0}", DateTime.Now.ToString()));
       
}
   
}



   
[Name("DepartureWeekdayRule"), Description("Rule that Validates Departure Day Matching Specified Weekday")]
   
[Tag("Departure"), Tag("CREATE"), Tag("EDIT")]
   
[Priority(9)]
   
public class DepartureRule: Rule
   
{        

       
public override void Define()
       
{            
           
RuleItem ruleItem = null;
           
DepartureRuleModel departureRuleModel = null;

           
// RulesInfo rulesInfo = new RulesInfo(true, "Departure Weekday Rule");

           
When()
               
.Match<DepartureRuleModel>(() => departureRuleModel)
               
.Match<RuleItem>(() => ruleItem, r => r.ClubID == departureRuleModel.ClubID, r => r.ResortID == departureRuleModel.ResortID, r => r.ValueField == departureRuleModel.DepartureDayOfWeek.ToString());
           
Then()
               
.Do(ctx => ctx.Insert(new RulesInfo(true, "Departure Weekday Rule")))
               
.Do(ctx => Console.WriteLine("Departure Weekday Rule Ran: {0}", DateTime.Now.ToString()))
               
.Do(ctx => ctx.Halt());
       
}
   
}





I've also created a Rules Info class to return values from the Rules:

    public class RulesInfo
   
{
       
public bool RuleValue { get; set; }
       
public string RuleMessage { get; set; }

       
public RulesInfo(bool RuleValue, string RuleMessage)
       
{
           
this.RuleValue = RuleValue;
           
this.RuleMessage = RuleMessage;
       
}
   
}





II also haso a "RuleItem" Class:
    public class RuleItem
   
{
       
[Key]
       
public int RuleItemID { get; set; }
       
public string Name { get; set; }
       
public string Description { get; set; }
       
public int? ClubID { get; set; }
       
public string ValueField { get; set; }
       
public int? ResortID { get; set; }
       
public bool Active { get; set; }
   
}


This is a "Code First" Class used by Entity Framework Core to store the Expected values of each Club -> Resort.
This is a sample of the Database entries for the Test Club -> Arrival and Departure expected values:

RuleItemID    ClubID    ValueField    Description                                          Name                         ResortID
1                  11          Saturday      The Test Resort Check-In Day Rule       Check-In Day Rule       19
2                  11          Saturday      The Test Resort Check-Out Day Rule    Check-Out Day Rule    19


This is the TEST API Method that ties it all together and where I'm finding issues:


   
[AllowAnonymous]
   
[HttpGet]
   
[Route("testrule")]
   
public string TestRule()
   
{

       
try
       
{
           
RuleItem arrivalRuleItem = null;
           
RuleItem departureRuleItem = null;

           
// Obtain the objects that contain the specified values
           
// that the Rules should expect.
            arrivalRuleItem
= GetBookingRuleItem(1);
            departureRuleItem
= GetBookingRuleItem(2);


           
// Load All Rules Tagged Create into a Rule Set
           
var repository = new RuleRepository();
            repository
.Load(x => x
               
.From(Assembly.GetExecutingAssembly())
               
.Where(rule => rule.IsTagged("CREATE"))
               
.To("CreateBookingRuleSet")      
           
);

           
// Compile Rule Set
           
var ruleSets = repository.GetRuleSets();
           
var compiler = new RuleCompiler();
           
ISessionFactory factory = compiler.Compile(ruleSets.Where(x => x.Name == "CreateBookingRuleSet"));

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

           
// Load Domain Models with Test values for now
           
// but will be supplied in request
           
ArrivalRuleModel arrivalRuleModel = new ArrivalRuleModel{
               
ClubID = 11,
               
ClubName = "Test Club",
               
ResortID = 19,
               
ResortName = "Test Resort",
               
ArrivalDayOfWeek = DayOfWeek.Saturday
           
};

           
// Domain Model for the DepartureRule
           
DepartureRuleModel departureRuleModel = new DepartureRuleModel{
               
ClubID = 11,
               
ClubName = "Test Club",
               
ResortID = 19,
               
ResortName = "Test Resort",
               
DepartureDayOfWeek = DayOfWeek.Saturday
           
};

           
           
// Insert items into rule engine's memory
            session
.Insert(arrivalRuleModel);                
            session
.Insert(arrivalRuleItem);

            session
.Insert(departureRuleModel);                
            session
.Insert(departureRuleItem);


           
// Fire the Rule which will
           
// return the number of Rules successfully fired.
           
var result = session.Fire();

           
// Obtain the Rule Info object from the Session, if available.
           
List<RulesInfo> rulesInfoList = session.Query<RulesInfo>().ToList<RulesInfo>();

           
// Remove items fron the Session
            session
.RetractAll(session.Query<ArrivalRuleModel>());
            session
.RetractAll(session.Query<DepartureRuleModel>());

           
// Destroy the Session
            session
= null;

           
if(result <= 0){
               
// Do something here......
           
}


       
}
       
catch (Exception ex)
       
{                
           
throw new ApplicationException(ex.Message);
       
}

       
return "Test Rule Completed...";
   
}





When the API method above is called and debugged, the Rule Set is obtained correctly.
However, the variable "result" on session.Fire() returns "3". and these are the entries in the Debug Console:

Arrival Weekday Rule Ran: 7/12/2019 10:03:39 AM
Arrival Weekday Rule Ran: 7/12/2019 10:03:39 AM
Departure Weekday Rule Ran: 7/12/2019 10:03:39 AM

The List variable "rulesInfoList" also displays 3 items in the Debug Console:

rulesInfoList
Count = 3
[0]:{exchangeprogram.backend.Rules.RulesInfo}
RuleMessage [string]:"Arrival Weekday Rule"
RuleValue [bool]:true
[1]:{exchangeprogram.backend.Rules.RulesInfo}



These are my questions:

  1. Am I misunderstanding how compiled Rule Sets are run and is my code correct?
  2. Why are some of the Rules running twice?
  3. How do I determine which Rules in a Rule Set failed when run?

Many Thanks for your help,

Leon

Leon Small

unread,
Jul 16, 2019, 1:38:44 PM7/16/19
to NRules Users
Hi Sergiy,

Should also mention that I'm using NRules Version 0.9.0.

Looking forward to your response for the above.

Regards,

Leon
Reply all
Reply to author
Forward
0 new messages