BusinessRules hanging around with unit tests

BusinessRules hanging around with unit tests

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


pearljam posted on Tuesday, September 03, 2013

I have a set of unit tests testing the properties and rules of my business object. My business object has a string property called Frequency, and I attach a rule to that property that checks the string against a regular expression. That regular expression is pulled out of a config file based on a value that gets passed into my objects constructor. In the test setup portion of my unit tests (MSTest), I create two instances of my business object. The only difference between the two is that the regular expression used in the frequency rule is different for each of the two objects. Now for each row of the test I set a value to the Frequency property of these two objects. What I am seeing is that when I set a value to either of the business objects, BOTH of the instances of the rule attached to the Frequency properties are run, and not just the one specific to each business object. I was thinking that this has something to do with the fact that they are both attached to the same property, even though there are two distinct business objects. The second thing I noticed is that every time a row is run for my test method, I recreate the the two business objects and the BusinessRules collection still maintains the rules from the previous test run. I have 8 rows for this test, so the list of rules gets quite large. By the time I get to the 8th row and my test sets a value to the frequency property those regular expression rules are running 8 times each! I must be doing something wrong??

JonnyBee replied on Tuesday, September 03, 2013

CSLA 4.x has type rules only.

Rules should be defined in
protected override AddBusinessRules()
and this method will only be called ONCE for each Type. 

IE each rule instance should be regarded as a singelton shared instance for ALL instances of the business object type. 

So you must rework your logic and may set the regular expression or key in a property of the BO and make the rule pull the regex based on this property.  

pearljam replied on Wednesday, September 04, 2013

Thanks Jonny, that does clear up some things, I don't think I quite understood the scope of the rules.

As another question, is there a way to remove or clear rules after they are added? Or check if a given rule exists before adding it?

JonnyBee replied on Wednesday, September 04, 2013

No, this does't fit with type rules that goes for all instances. You cannot remove or clear rules after they have been registered. 

You may however create several complete rulesets. 

 

 

tiago replied on Friday, September 06, 2013

pearljam

As another question, is there a way to remove or clear rules after they are added? Or check if a given rule exists before adding it?

As Jonny said, under CSLA 4.x "each rule instance should be regarded as a singelton shared instance for ALL instances of the business object type" and it can't be removed, or added twice. So once a rule is set, it is set for good.

This is a major change to the way Csla 2.x and 3.x used to handle rules as they allowed instance rules. Instance rules could slow down your application a lot. Of course the flexibility of instance rules is lost, unless you find a way to make the rule behave differently according to some settings.

Say you have a MyBusinessObject  and on its AddBusinessRules() method adds a MyBusinessRule class. Now you want the MyBusinessRule class to do different things according to some settings.

 

1) You should create a MyBusinessRuleSettings class that holds a few properties like

public class MyBusinessRuleSettings : MobileObject

{

bool ShouldSneeze { get; set; }

bool ShouldCough { get; set; }

bool ShouldShout { get; set; }

MobileList<string> ShoutExpressionCollection { get; set; } /* note the use of MobileList<> instead of List<> */

}

 

2) On the MyBusinessObject  add a property to keep your settings at hand

private static readonly PropertyInfo<BusinessRuleSettings> RuleSettingsProperty = RegisterProperty<BusinessRuleSettings>(p => p.RuleSettingsProperty);

public BusinessRuleSettings RuleSettings

{

    get { return ReadProperty(RuleSettingsProperty); }

    set { LoadProperty(RuleSettingsProperty, value); }

}

 

3) On the Execute(RuleContext context) method of the MyBusinessRule class get your settings values like this

_businessObject = (MyBusinessObject) context.Target;

_ruleSettings = _businessObject.RuleSettings;

 

4) All you need is to decide the best place to populate your rule settings. If the rules depend on the specific object, they should be loaded from database at the same time you load your object values (DataPortal_Fetch). For new objects, you should get your default rule settings from the database on the DataPortal_Create method.

<edit>

Forgot to say that in your MyBusinessRule class you must tell Csla not to cache the rule result, so it re-evaluates it every time. Like this:

public new bool CacheResult

{

get { return false; }

}

</edit>

Copyright (c) Marimer LLC