Validation of a BLB

Validation of a BLB

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


joemorin73 posted on Friday, July 30, 2010

I have a a BusinessObject that needs to validate that one of it's collection contains atleast one.  I tried making the following class and add it to my Rules:

        public class OfficeRequired : Csla.Rules.BusinessRule
        {
            public OfficeRequired(Csla.Core.IPropertyInfo officesProperty) : base(officesProperty)
            {
                InputProperties = new List<Csla.Core.IPropertyInfo> { officesProperty };
            }

            protected override void  Execute(RuleContext context)
            {
                var cOffices = (MemberOfficeList)context.InputPropertyValues[PrimaryProperty];
                if (cOffices.Count == 0)
                    context.AddErrorResult(PrimaryProperty, "Member can not be saved without an office.");
            }
        }

Add rule:

            BusinessRules.AddRule(new OfficeRequired(OfficesProperty));

OfficesProperty is a BusinessListBase.

Problem is adding and removing from the collection does not trigger the rule to be verified.  I have found a patch around this for now, but I'm hoping for something a little cleaner.

What is the best approach for this? 

RockfordLhotka replied on Friday, July 30, 2010

This rule is in the parent object (the object that contains the child list object) right?

You need to invoke the rules for this property yourself in a ChildChanged event handler (or OnChildChanged override) in this parent object. Just call BusinessRules.CheckRules(OfficesProperty) to run the rules for that property when a child has changed (or when that specific child has changed).

joemorin73 replied on Friday, July 30, 2010

Funny you should mention this, almost exactly what I did.

         Offices.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Offices_CollectionChanged);
        void Offices_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            PropertyHasChanged(OfficesProperty);
        }
To be honest, I used CollectionChanged instead of ChildChanged because I didn't notice the latter.  Am I correct to assume ChildChange triggers on any change in the child objects in the list while CollectionChange will trigger on only the addition or removal of items in the collection?

On another note, as a possible feature, is it possible to have CSLA incorporate this in the framework?  Say, AddCollectionRule and have CSLA bind to the event?

RockfordLhotka replied on Friday, July 30, 2010

It is a change worth considering perhaps. The thing is that there's some cost to calling CheckRules because the framework has to locate the appropriate rule list, even if it is empty.

So consider a complex object graph with parent, child, grandchild and greatgrandchild objects. Change a greatgrandchild, and you'd end up with CheckRules being run at 5 levels.

Perhaps the perf cost would be so low you wouldn't notice it. Or perhaps not.

To date I've considered child-triggered rules to be uncommon enough that people can just handle ChildChanged and explicitly call CheckRules. This is the optimal solution performance-wise, but does require specialized knowledge of this technique (as you've noticed).

So perhaps someday I'll make the opposite choice - have everyone pay a small perf cost for this convenience.

If this sort of rule was the norm - if 90% of all parent objects had such rules - I'd have done it already. But I rather suspect it is far less than 10% of all parent objects that have such rules, so the perf vs effort decision has balanced (thus far) in favor of perf.

bmmathe replied on Thursday, August 05, 2010

I'm implementing  the same thing actually.  In fact what I've noticed is the opposite, most of our parent objects do have validation rules that apply to the child objects.  For example, a customer must have a phone number.  In our case phone numbers are a child collection, a customer must have one phone number and can have many.  We might want to constrain this by adding a collection level rule that states "a customer can not have duplicate phone numbers" and "a customer can not have multiple home phone numbers".  I'm guessing these rules would be applied when the collection changes.

Another example is an invoice.  Can you have an invoice without line items?  I think the most prevalent rule would be "this child collection must have at least one".  It would probably be worth looking into a way to easily add business rules at the collection level.  The "Must require 1 child" rule would even be a "CommonChildCollectionRule".

RockfordLhotka replied on Thursday, August 05, 2010

The problem with adding rules actually in the collection itself, is that there's no way to express those rules into the UI. There is no data binding mechanism by which a collection can say it is invalid.

In other words, I could implement a rules mechanism in BLB - and then you could write your rules using it - and then this thread would be about the sad reality that there's no way for your users to know that the rules are broken...

So then I'd have to invent mechanisms to display broken rules in every UI technology Microsoft provides (all 5 or more of them). That's not going to happen...

Ultimately this means that, if you need collection-level rules, you must implement them in the parent of the collection - if for no other reason that this enables you to display the broken rules to the user using existing binding models for the various UI technologies.

The only thing you need to do to make this work, is override OnChildChanged() and call BusinessRules.CheckRules(yourProperty).

Copyright (c) Marimer LLC