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:
- Am I misunderstanding how compiled Rule Sets are run and is my code correct?
- Why are some of the Rules running twice?
- How do I determine which Rules in a Rule Set failed when run?
Many Thanks for your help,
Leon