BrokenRulesCollection empty even though RuleContext shows otherwise

BrokenRulesCollection empty even though RuleContext shows otherwise

Old forum URL: forums.lhotka.net/forums/t/11921.aspx


TSF posted on Friday, April 05, 2013

I'm trying to work my way through a gateway rule. As I step through the Execute method of the Inner Rule, I can see the failure result added to the rule context. After being added, back in the gateway/outer rule I can then see that the RuleContext contains a count of 1 for the Results property. And that one RuleResult  has its Severity property value = "Error" and its Success property value = False.

Back in my test class, however, the object's IsSavable property is True. It should be False.

Here is my test method. If PaymentTermNumber = 10 then ExtendedDate must be greater than Today.

var bo = Order.NewEditableRoot();           
bo.PaymentTermNumber = 10;           
bo.ExtendedDate = DateTime.Today.AddDays(-5);                      
Assert.IsFalse(bo.IsSavable); 

What am I missing? Thanks.

JonnyBee replied on Friday, April 05, 2013

Does your broken rule contain a messsage?

You MUST have a message to the user. 

Also make sure that the RuleURI is unique for the gateway rule - if you get non-unique RuleURI´s the last rule to execute wins. 

TSF replied on Monday, April 08, 2013

From what I can tell, it does (assuming I have done this properly). Here is the rule in my object:

BusinessRules.AddRule(new Equal(
                           PaymentTermNumberProperty
                           , 10
                           , new GreaterThan(ExtendedDateProperty, DateTime.Today))
{ MessageText = "Extended Date must be greater than Today when Payment Term is 'Tiered'" }); 

And here is what I see after the inner rule has executed:

 

[UPDATE] How do I make certain my Rule URI is unique?

TSF replied on Monday, April 08, 2013

To provide more information in case it is helpful, here is the "outer" or primary rule constructor:

        public Equal(IPropertyInfo primaryProperty, IComparable compareToValue, IBusinessRule innerRule)  : base(primaryProperty)
        {
            CompareToVal = compareToValue;
            this.RuleUri.AddQueryParameter("compareToValue", compareToValue.ToString());
            InputProperties = new List<IPropertyInfo>() { primaryProperty };

            InnerRule = innerRule;
            this.RuleUri.AddQueryParameter("rule", System.Uri.EscapeUriString(InnerRule.RuleName));

            // merge InnerRule input property list into this rule's list
            if (InnerRule.InputProperties != null)
            {
                InputProperties.AddRange(InnerRule.InputProperties);
            }

            // remove any duplicates
             InputProperties = new List<IPropertyInfo>(InputProperties.Distinct());
            AffectedProperties.AddRange(innerRule.AffectedProperties);
        } 

And here is that rule's Execute method:

        protected override void Execute(RuleContext context)
        {
            var value1 = (IComparable)context.InputPropertyValues[PrimaryProperty];

            var chainedContext = context.GetChainedContext(InnerRule);
            InnerRule.Execute(chainedContext);
         }

Next is the Inner Rule's constructor:

        public GreaterThan(IPropertyInfo primaryProperty, IComparable compareToValue) : base(primaryProperty)
        {
            IsPropertyComparison = false;
            CompareToVal = compareToValue;
            this.RuleUri.AddQueryParameter("compareToValue", compareToValue.ToString());
            InputProperties = new List<IPropertyInfo>() { primaryProperty };
        } 

And finally, here is the Inner Rule's Execute method:

        protected override void Execute(RuleContext context)
        {
                var value1 = (IComparable)context.InputPropertyValues[PrimaryProperty];

                if (value1.CompareTo(CompareToVal) <= 0)
                {
                    context.Results.Add(
                            new RuleResult(this.RuleName
                                        , PrimaryProperty
                                        , string.Format(GetMessage(), PrimaryProperty.FriendlyName, CompareToVal.ToString())) { Severity = this.Severity });
                   
                 }
        } 

JonnyBee replied on Monday, April 08, 2013

Aha, which version of CSLA do you use?

IIRC - this was fixed in CSLA 4.2. It _would_ seem to work in previous 4.x version but when the rule engine rechecks properties for affected properties (your ExtendedDateProperty) all brokenrules for this property is cleared before rules is rechecked. And so your broken rules disappears.

For CSLA 4.2 I added OriginProperty to BrokenRule in order to support a rule setting broken rules on another property (ie - BrokenRulesCollection.ClearRules will only clear rules that originated from the property to be checked - and NOT all broken rules where PrimaryProperty = propertyToBeChecked). 

IE: Pre CSLA 4.2 the rule engine failed when a rule tried to set broken rule on another property than PrimaryProperty (of the registered rule). 

Actual issue in bugtracker:
http://www.lhotka.net/cslabugs/edit_bug.aspx?id=905

TSF replied on Monday, April 08, 2013

Thanks, Jonny. That is helpful to know about version differences, although I was using the latest (4.5.10). But in the process of looking at what you were saying about the ClearRules method, I stumbled upon an error in my rule logic. Once I address that, it worked. Thanks, again.

Copyright (c) Marimer LLC