CSLA 4 business rules changes

CSLA 4 business rules changes

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


RockfordLhotka posted on Monday, January 04, 2010

In CSLA .NET 4.0 I plan to make some fairly major changes to the business rules subsystem. This will include breaking changes, but I think they are necessary and good breaking changes. Here’s the bugtracker issue for the change

http://www.lhotka.net/cslabugs/edit_bug.aspx?id=623

At a high level my goals are

  1. Support shared hosting scenarios
  2. Change naming/coding to better reflect business rules, not just validation
  3. Provide consistency between sync and async rules
  4. Allow a rule to add/remove multiple broken rule entries (enable rule chaining)

As you can imagine, these changes will have at least some impact on existing rule methods and AddBusinessRules() methods. I hope that the changes are pretty mechanical (and thus easy to make), but this will improve readability and open up some new scenarios that are hard/impossible today.

My current thinking includes a few changes:

  1. Rename the ValidationRules protected property to BusinessRules
  2. Eliminate the bool return from rule methods, instead returning the result via RuleArgs with the default being true (so business rules can ignore the whole thing, while validation rules will set e.Result = false for failure)
  3. Provide a RuleContext value in RuleArgs that allows you to get/set property values from the target object in an abstract manner, much the way async rules work today, but bi-directional
  4. With sync and async rules, after the rule completes (so the code is running on the UI thread), update changed property values from RuleContext back into the object
  5. Maybe call a lambda (Action<T>) after the rule completes and the code is running on the UI thread (whether sync or async)
  6. Allow "rule chaining", where a rule can call other rules, and can aggregate the results of those rules into RuleArgs so post-processing occurs against all the results
  7. Possibly use MEF to dynamically discover and load rule methods in a more abstract manner
  8. Add the concept of "rule domains", so a business type can have multiple rule sets, each belonging to a domain - this would support shared hosting scenarios for server apps and app servers
  9. If I can get the sync/async rule syntax to match by using RuleContext, I may use an [AsyncRule] attribute so you can indicate that a rule is required to run async by putting that attribute on the rule method
  10. Add a BusinessRuleAttribute (subclass of ValidationAttribute) that implements ObjectFactory-like protected members, making it easier to create a rule attribute that can get/set values in the target object

You can imagine AddBusinessRules() looking more like this:

protected override void AddBusinessRules()
{
  base.AddBusinessRules();
  BusinessRules.AddRule(MyRule, MyProperty);
  BusinessRules.AddRule(MyRule, MyOtherProperty);
}

And you can imagine a rule looking like this:

private static void MyRule<T>(T target, RuleArgs e) where T : MyType
{
  if (some condition is met)
    return;
  else
  {
    e.Description = "oops";
    e.Result = false;
  }
}

or a mutator rule like this:

private static void MyRule<T>(T target, RuleArgs e) where T : MyType
{
  e.RuleContext.PropertyValues[e.PropertyName] = 
    e.RuleContext.PropertyValues[e.PropertyName].ToString().ToUpper();
}

rasupit replied on Monday, January 04, 2010

Rocky,
I also would like to also see support for validation rules separation in csla 4.0.  Like ObjectFactory, this would make it easier to test the validation logic. 

The implementation could look something like the following: 
        private static MyBoValidator rule = new MyBoValidator();

        protected override void AddBusinessRules()
        {
            BusinessRules.AddRule(rule.MyRule, MyProperty);
        }
The separated validation rule can be implemented as follow
    class MyBoValidator
    {
        public void MyRule(MyBo target, RuleArgs e)
       {
             //validation code goes here
        }
    }

If we can access bo's protected members from RuleContext, then I guess the above implementation will just work with the new 4.0 validation.  If not then csla 4.0 can help by creating base class for validator that implements the ObjectFactory's protected methods.

Thanks,
Ricky

RockfordLhotka replied on Monday, January 04, 2010

The only trick with that is it requires creating an object instance. The rule method can't be static, it has to be an instance method, and CSLA will need to create the instance.

Using a singleton scheme would probably be best - but will cause support headaches, because developers aren't generally used to coding singletons.

Of course you can do this today. You could implement a singleton object that inherits from ObjectFactory and exposes rule methods. So maybe I don't really need to do any work, since the feature already exists :)

 

rasupit replied on Tuesday, January 05, 2010

RockfordLhotka:
The only trick with that is it requires creating an object instance. The rule method can't be static, it has to be an instance method, and CSLA will need to create the instance.


Yes, which is the intent so that we can easily test the validation rules in isolation which is very usefull for complex validation.  Like ObjectFactory, I'm seeing this method can be another alternative to in object static method, and users who are not familiar with singleton pattern don't need to use it (they probably don't do test. just kidding...;))

Agree, we can do this today by inheriting from ObjectFactory but it nice to have "official" class, so that it doesn't sound hacky.

@jonnybee,
Yes, we can use this method to create common validation rules within some bounded context.  That's a very nice example.

Ricky

JonnyBee replied on Tuesday, January 05, 2010

Hi Ricky,

You could even do it this way:
    public static class MyCommonRules
    {
        private class Accessor : ObjectFactory
        {
            internal IDisposable BypassPropertyChecks(object o)
            {
                return base.BypassPropertyChecks((BusinessBase)o);
            }

            internal object ReadProperty(object target, string property)
            {
                var propertyInfo = PropertyInfoManager.GetRegisteredProperties(target.GetType()).Where(p => p.Name == property).FirstOrDefault();
                var fieldManager = (FieldDataManager) MethodCaller.CallMethod(target, "get_FieldManager");
                if (propertyInfo != null && fieldManager.FieldExists(propertyInfo))
                {
                    var info = fieldManager.GetFieldData(propertyInfo);
                    if (info != null)
                        return info.Value;
                    else
                        return null;
                }

                return MethodCaller.CallPropertyGetter(target, property);
            }
        }


        public static bool SmartDateIsFuture(object target, RuleArgs e)
        {
            var accessor = new Accessor();
            using (accessor.BypassPropertyChecks(target))
            {
                var value = (SmartDate) accessor.ReadProperty(target, e.PropertyName);

                if (value < DateTime.Today) {
                    e.Description = string.Format("{0} must be larger than {1}", e.PropertyFriendlyName, DateTime.Today);
                    return false;
                }
                return true;         
            }
        }
    }


The "hack" with methodcaller allows me to implement my own non-generic ReadProperty in my accessor. If ObjectFactory would (in future) include a non-generic ReadProperty then I'd rather use that call to read actual fieldvalue.

I want to get acces to the FieldValue because of  SmartDate (and other custom structs) where the FieldData and actual property are different types (ex SmartDate and string). When properly coded in BO this will also work with private backing fields (old style) and ManagedProperties with private backing field.

rsbaker0 replied on Tuesday, January 05, 2010

If you are going to make breaking changes, perhaps you could consider:

1. Enforcing rule priority across properties. Currently short-circuiiting can't be used to guarantee the order that rules execute when property dependencies are present.

2. Provide for the capability to manually execute a specific rule versus only based on a property change. If the rule so invoked fails, it gets added to the broken rules collection, if it succeeds, then it is removed from the broken rules collection.

rxelizondo replied on Tuesday, January 05, 2010

Well unfortunately, I don’t really have enough deep understanding of the CSLA to give you any meaningful feedback but I trust you know what you are doing and I will simply adapt to whatever changes you have in mind.


Nevertheless, if you don’t mind me suggesting something, what I would personally like to see would be the complete integration of the PropertyInfo, RulelMethod and RulesArgs. In other words, I don’t like to do this:


ValidationRules.AddRule(CommonRules.StringMaxLength, new ommonRules.MaxLengthRuleArgs(CategoryProperty, 30));


There are too many mismatch things that could occur here (from what I can tell):


1. CategoryProperty could be a property that represents a data type that is not a string (we are expecting it to be a string in the example above)
2. The “AddRule” method can take anything that derives from RuleArgs (So we could pass a RuleArgs used to validate DateTimes to validate a string).
3. The rule method is expected to match the RuleHandler signature so it can be anything.


So to give an example, the code above could be changed to the code shown below and I will only get a runtime error but not compile time error:


ValidationRules.AddRule(CommonRules.StringMaxLength, new CommonRules.IntegerMaxValueRuleArgs(CategoryProperty, 30));


Notice how I am using “IntegerMaxValueRuleArgs” for a “StringMaxLength” method. Clearly this is not something that you want to do.


So in my world, I would prefer to have a single (all in one) class for validation like the snippet show below:


------------------------------------------

public class NiceStringMaxLength : DecoratedRuleArgs
{
public NiceStringMaxLength(PropertyInfo<string> propertyInfo, int maxLength)
: base(propertyInfo)
{
this["MaxLength"] = maxLength;
this["Format"] = string.Empty;
}

public int MaxLength
{
get { return (int)this["MaxLength"]; }
}

public virtual bool CheckRule(object target)
{
int max = (int)this["MaxLength"];
string value = (string)Utilities.CallByName(target, this.PropertyName, CallType.Get);
if (!String.IsNullOrEmpty(value) && (value.Length > max))
{
string format = (string)this["Format"];
string outValue;
if (string.IsNullOrEmpty(format))
outValue = max.ToString();
else
outValue = max.ToString(format);
this.Description = String.Format(Resources.StringMaxLengthRule, RuleArgs.GetPropertyName(this), outValue);
return false;
}
return true;
}
}

------------------------------------------

So I could be totally type safe and add a validation rule like this:


ValidationRules.AddRule(new NiceStringMaxLength(CategoryProperty, 30));


NOTE: The code above is totally untested. Something tells me that I am suggesting here would not fly so I didn’t apply myself here.


Thanks.

JoeFallon1 replied on Tuesday, January 05, 2010

Rocky,

One of my key concerns with mutator rules is that setting the Property of the BO causes all of the rules to run again. There were various suggestions to get around this issue including short circuiting and using rule priorities. But it would nice if the framework handled all of this for the developer so that they could concentrate on the rules themselves without being concerned about how many times they get executed.

1. Will your proposed changes take this into account?

2. Will mutator rules always run before Validation rules? (Why validate something that you are about to change?)

Joe

 

RockfordLhotka replied on Tuesday, January 05, 2010

This is all good input - which is what I'd hoped for when starting the thread :)

Andy and Joe, I don't have answers for whether my proposed changes will do these things. They weren't on my list, and I'm glad you are bringing them up.

One thing that is on my list is to make sync/async and validation/mutation rules all look and work the same. So making mutator rules run first would require making them NOT look the same so CSLA could detect them and default them to a lower priority or something.

An earlier post also suggested (basically) merging all rules from multiple (dependent) properties into a consolidated list, sorting them by priority and then running them as a single list - thus merging all short-circuiting from all properties into a single run. I think this would be an unworkable mess - but I'm interested to hear what others think about the idea.

tmg4340 replied on Tuesday, January 05, 2010

While I think that making validation and mutation rules "all look and work the same" is perfectly fine (and also much wanted), I think you can't process them the same, for many of the reasons already listed.  As Joe mentioned, there's little point in running a validation rule on a property that's going to be mutated later.  And I also see value in Andy's (and others') suggestion to allow for specific scenarios where sets of rules are run, or allow for certain sets of rules to be run programmatically.  Creating a rule type would also allow you to relatively-easily insert some latch functionality into the mutator processing, thus satisfying Joe's request.

Perhaps this is something that could be shoehorned into the rule-domain concept.  Rule domains are essentially a way to group rules, so you could essentially pre-create two domains.  Then you can keep much of the semantics of rule construction identical.  Perhaps you weren't considering the concept of nested domains, but it seems to me to be a logical extension that wouldn't add a ton of overhead.

I also see a lot of value in rxelizondo's suggestion of matching up rules with types.  I've been bitten by that bug a few times...

HTH

- Scott

RockfordLhotka replied on Tuesday, January 05, 2010

Let’s not forget the sync/async issue too.

 

At least today, sync rules run synchronously, according to priority. Async rules (only validation today) are then launched at the end and finish when they finish. There’s no sense of priority for async rules, because they run concurrently and asynchronously.

 

I plan to enable async mutator rules, which will STILL run at the end and will finish when they finish. A mutator rule will be able to change multiple properties (it will return a dictionary of changed values). But since it is async, its completion is indeterminate – we don’t know when it will get done.

 

That adds a very interesting wrinkle into this whole discussion – as you all ask for more control, the async world is providing (arguably) less control.

ajj3085 replied on Tuesday, January 05, 2010

Async mutator rules sound like a problem waiting to happen.

What happens when two async rules change the same property to different values?  Which rule "wins" in that case? 

ajj3085 replied on Tuesday, January 05, 2010

Couldn't both the "run only on property changed" and "this is a mutator" rule be handled by adding a parameter to the AddRule call, indicating that this should should only run on property changed or that it's a mutator?

RockfordLhotka replied on Tuesday, January 05, 2010

Or using attributes on the rule method?

ajj3085 replied on Wednesday, January 06, 2010

Yes, I think an attribute would work fine too. 

RockfordLhotka replied on Tuesday, January 05, 2010

ajj3085:

Couldn't both the "run only on property changed" and "this is a mutator" rule be handled by adding a parameter to the AddRule call, indicating that this should should only run on property changed or that it's a mutator?

Which ever mutator ends last would win. It is true that multiple async mutator rules would be problematic.

But there are two features that I think will offset the issue:

  1. Rule chaining
  2. Post-processing callback

Rule chaining means that one rule can create multiple brokenrule entries and/or can call other rules. The primary motivation here is to allow creation of a rule that invokes an external rule engine that creates numerous broken/unbroken rule results.

Post-processing callback means that you can provide a delegate/lambda that is invoked when a rule is complete. This will occur on the UI thread (or so goes my plan) so you can do any post-processing you'd like to do.

Combine post-processing with chaining and you should be able to invoke a set of syncrhonous rules upon completion of an async rule.

tmg4340 replied on Wednesday, January 06, 2010

RockfordLhotka:
Which ever mutator ends last would win. It is true that multiple async mutator rules would be problematic.

But there are two features that I think will offset the issue:

  1. Rule chaining
  2. Post-processing callback

Rule chaining means that one rule can create multiple brokenrule entries and/or can call other rules. The primary motivation here is to allow creation of a rule that invokes an external rule engine that creates numerous broken/unbroken rule results.

Post-processing callback means that you can provide a delegate/lambda that is invoked when a rule is complete. This will occur on the UI thread (or so goes my plan) so you can do any post-processing you'd like to do.

Combine post-processing with chaining and you should be able to invoke a set of syncrhonous rules upon completion of an async rule.

Given that "multiple async mutator rules would be problematic", this would seem to be another point in favor of separating the two types of rules.  This way, when your SL app asynchronously launches the business-rule validation process, the server-side code can process the mutation rules first - and synchronously - before launching any validation rules.  This also feeds into the "don't validate before mutating" concept as well.  The way I read it, neither rule chaining or post-processing callbacks solve this issue.  But maybe I'm missing something.

- Scott

mbblum replied on Tuesday, January 05, 2010

RockfordLhotka:

An earlier post also suggested (basically) merging all rules from multiple (dependent) properties into a consolidated list, sorting them by priority and then running them as a single list - thus merging all short-circuiting from all properties into a single run. I think this would be an unworkable mess - but I'm interested to hear what others think about the idea.



This is a "nice to have", but to me a lower priority to be in CSLA than the other items. I have coded brute force walk of the BrokenRulesCollection down the object hierarchy to get a list for display.

What I would request is a property that can be used to order the display of broken rules. One implementation is a integer SortOrder property, default = 0, on the BrokenRule, which can be set from a RulesArg property of the same name, i.e. e.SortOrder = 10.

The Severity allows grouping into Error, Warning, Information. This can simplify display sorting when required for a use case wanting specific BrokenRules to be at the top of the list.

The other comments are looking like good improvements.

Thanks.

RockfordLhotka replied on Tuesday, January 05, 2010

mbblum:
RockfordLhotka:

An earlier post also suggested (basically) merging all rules from multiple (dependent) properties into a consolidated list, sorting them by priority and then running them as a single list - thus merging all short-circuiting from all properties into a single run. I think this would be an unworkable mess - but I'm interested to hear what others think about the idea.

This is a "nice to have", but to me a lower priority to be in CSLA than the other items. I have coded brute force walk of the BrokenRulesCollection down the object hierarchy to get a list for display.

I think you misunderstand the question.

The earlier post suggests a change to the way rules are EXECUTED, so rules from dependent properties are merged into a single ordered list, then executed. So a rules for properties A, B and C might run all intermixed, and a short-circuit in property B would stop rules for A and C.

----

There is a backlog item for a consolidated brokenrules collection feature. That's an entirely different thing, and I may add that - but I agree that it is a relatively low priority since you can do this today with little effort.

The sort order idea is interesting though.

mbblum replied on Wednesday, January 06, 2010

Regarding earlier post, you're correct that I misunderstood. But my main request to provide for ordering the resulting broken rules came through.

Here is another "wish list" item. Business Rules that run on the common DataPortal activities, and can be specified to run before or after there execution. As I look at this, it can also be viewed as a request to make DataPortal_OnDataPortalInvoke() and DataPortal_OnDataPortalInvokeComplete() available via Business Rules.

One problem area might be determining when to run client side, server side or both.

(If you need more scenario, let me know.)
Thanks, mbb

rsbaker0 replied on Wednesday, January 06, 2010

mbblum:
...
Here is another "wish list" item. Business Rules that run on the common DataPortal activities, and can be specified to run before or after there execution. As I look at this, it can also be viewed as a request to make DataPortal_OnDataPortalInvoke() and DataPortal_OnDataPortalInvokeComplete() available via Business Rules....


I was going to bring up something like this yesterday as there have been prior forum discussions on having server-side rules, but I thought there might not be much interest (and it might merit a thread on on its own).

I can certainly see a possible use for rules that don't get executed until you actually try to save the object (and then might even need to run server side if they are "expensive"). However, I'm not sure how this fits in with the CSLA model, which seems to assume that an object will generally save successfully as long as IsValid is true and you then replace the original reference with the returned object. If rules break "later", you either have to return the object unsaved (which UI would need to new code to test for) or handle an exception (in which case the deferred broken rules may not be associated with the original object any more but instead on the copy returned in the DataPortal exception).

ajj3085 replied on Tuesday, January 05, 2010

Since you're going to be rework this anyway..

Could there be a way where:

1.  Rules run just like they do now

2.  Rules ONLY which run in a response to a Property changed (SetProperty).

#2 seems most useful the the "mutation" style rules, which alter values.  While you may want to validate data as you read it from your database, these rules I would think wouldn't need to be run.  In the past, I've been bitten by this; a Password property which encypted via a rule method would attempt to reencrypt when the instance was loaded from the database.  There were other validation rules that I did have to run, so I had to hack together a solution.

I've hit a few other similar scenarios like this as well.

RockfordLhotka replied on Wednesday, January 06, 2010

Async rules exist on .NET and SL. And they aren't just server-side rules. They are just async rules - could run on client or server.

Today async rules are validation only - there's no model by which the rule can change the object. My plan is to follow the lead of WF (more or less) and allow the rule to build a Dictionary<PropertyInfo, object> and once the rule is done and processing is on the UI thread, CSLA will update the properties with the values in that dictionary. The result is a safe mutator rule that does its work on a background thread or a server or whatever.

What I hear you saying Scott, is that I shouldn't do this. Shouldn't enable async business rules at all.

But I don't think that's an entirely valid solution, because today you can't do long-running calculations on a background thread, and you can't do database lookups (user enters field A, we go find the value for field B) on SL, etc.

I really think there's a need for async business rules.

tmg4340 replied on Wednesday, January 06, 2010

I wasn't trying to say we shouldn't have async rules.  I know that async rules are necessary (i.e. pretty much the only way to do things) in SL, and they have real benefits in traditional client/server situations.  I don't work in SL, but I've found async rulse to be a pretty slick feature in my WPF stuff.

I just think that if we're going to create rules that specifically change property values, then there should be some sort of mechanism to control the execution of those rules, so that the validation rules are ensured of working with "valid" property data - i.e. data that has already gone through any mutation rules.  As Joe mentioned in a previous post, why validate data that's going to change after the validation rules run?

Yes, maybe the change is nothing more than an upper-case rule - but maybe it's not.  Maybe it's changing "1M" to "1,000,000", and there's a validation rule that checks to make sure the data is numeric (since we've essentially converted a numeric field in the database to a textual field in the UI.)  If I can't guarantee that the mutator rule has run, then my validation rule has to be able to deal with non-mutated data, and I've got basically the same code in two places.

I know this kind of thing can, and maybe should, be handled differently (i.e. maybe this is handled in a property-change event instead), so perhaps this is a bad example.  But if you're going to allow the concept of mutator rules, then a part of me says that this kind of property-change code should be put there - after all, the ability to enter "1M" is a business rule, isn't it?  And if it's in a mutator business rule, then I only have to write the code once, instead of in each object where I need to accept those values.

HTH

- Scott

ajj3085 replied on Wednesday, January 06, 2010

I think what Rocky said though is that you would change your validation rule to run after your mutation rule, and that scenario will work out fine.

JoeFallon1 replied on Wednesday, January 06, 2010

I do not know if I am worrying about nothing here but the idea of re-running validation rules after mutator rules bothers me. It becomes very easy to get lost in the stack of rules that is running multiple times because the underlying Property value keeps changing due to the mutator rules.

For example say I have 10 validation rules and 4 mutator rules. The property changes in the UI and I expect 14 rules to run. But if the 4 mutator rules run at the end then the validation rules will end up running 50 times and the first mutator rule will run 5 times, the second 4 times, the third 3 times and the fourth 2 times. (Assuming that a mutator rule causes the set of rules to be re-run right away.)

The simplest idea is to always run the 4 mutator rules first and then run the 10 validation rules once afterward.

Now if Rocky can devise a way to set the value of a Property in a mutator rule without causing the rules to be re-run then perhaps the re-running of rules is not such a big deal. I can envision running the 14 rules and then re-running all of them because a mutator rule has run. A second run of rules maybe isn't too bad - I just don't want to see multiple runs. After all some of these rules hit the database and are expensive!

Joe

ajj3085 replied on Wednesday, January 06, 2010

I don't see anything that suggests the rules would be more than once, unless you somehow set it up to do that.  I doubt he's planning to rerun rules when a property is changed in response to a mutator business rule.

JoeFallon1 replied on Wednesday, January 06, 2010

ajj3085:

I don't see anything that suggests the rules would be more than once, unless you somehow set it up to do that.  I doubt he's planning to rerun rules when a property is changed in response to a mutator business rule.

Well, he may not be planning it but that is the way the current system works. Which is why I am concerned about it.

Joe

 

ajj3085 replied on Thursday, January 07, 2010

I'm not sure I follow, because it doesn't seem like the current system is like that.  In SL, there are no such thing as mutators, and for .Net, it depends on how you setup your rules.  If you're using LoadProperty or setting a private backing field, I don't see why the rules would run again.

rsbaker0 replied on Thursday, January 07, 2010

^^^^

In the current system, if a business rule changes the value via the setter, then I believe the same rule gets run again.

As you noted, you could use a private backing field or LoadProperty to avoid this, but now you've made a property change that escapes validation by other rules that might actually be interested in the change.

If the mutator rules for a property change ran first, then the changes made would be validated either way. I suppose you can simulate this just be giving the mutator rules priority so they run first (but then I'm not sure how dependencies come into play)

ajj3085 replied on Thursday, January 07, 2010

Right, I should add that I use priorities to ensure that mutator rules always run before validation type rules as well.

Of course, I haven't dived into async rules, so perhaps that's where we're encountering different expereiences, but again my understanding is that right now there is no way to do a mutator rule asyncly.  I can see this being a problem with async mutator rules, but it sounds like Rocky believes rule chaining will take care of this problem.

RockfordLhotka replied on Thursday, January 07, 2010

ajj3085:

I can see this being a problem with async mutator rules, but it sounds like Rocky believes rule chaining will take care of this problem.

Well, Rocky believes that someone who knows what they are doing will be able to use rule chaining to implement their scenario.

I don't think there's any magic here - async and/or multi-threaded software is always more complex.

But just because async business rules will allow people to screw up doesn't mean I shouldn't enable the scenario. When used carefully and knowledgably, the results should be pretty cool :)

ajj3085 replied on Thursday, January 07, 2010

Sorry, I don't mean to be putting words in your mouth.  We're all just trying to work out what the changes mean I think. 

I agree that the async rules should be an option... it seems pretty likely someone will want to do just this, and if its not there will be stuck.

RockfordLhotka replied on Thursday, January 07, 2010

Today it depends on whether you are writing a private or public rule.

Private rules can easily use BypassPropertyChecks, LoadProperty or a private backing field.

Public rules don't have that luxury unless you build them as instance rules in an ObjectFactory subclass.

I don't plan to change any of that directly.

But as I've said, rules will be able to create a dictionary of output values. That dictionary will be used by CSLA to update the properties specified in the dictionary. Presumably this will be done by calling LoadProperty, but will happen before the post-processing callback.

In other words, the post-processing callback will run after the property values are updated, allowing you to trigger or explicitly execute validation rules on the mutated value(s).

ajj3085 replied on Thursday, January 07, 2010

So what will that triggering look like?  Right now its ValidatoinRules.CheckRules.. which will run everything again, or passing a property name will run just for that property, but may run everything for that property again.

Is there going to be something like a RegisterRule method, and each rule will have a token of some kind (like the PropertyInfo for properties) so that we can selectively run rules, or is this where the "rule domain" concept fits in?

JoeFallon1 replied on Thursday, January 07, 2010

RockfordLhotka:

Today it depends on whether you are writing a private or public rule.

Private rules can easily use BypassPropertyChecks, LoadProperty or a private backing field.

Public rules don't have that luxury unless you build them as instance rules in an ObjectFactory subclass.

This is the point I was making about the re-running of rules. I stated it explicitly in the original thread which the bug ID refers to. When I centralize a rule I have to reflect against the BO and set a Public Property which causes the rules to re-run. A private rule inside a BO can use any of the other techniques to set the backing field and avoid the Property Set.

Joe

 

ajj3085 replied on Thursday, January 07, 2010

Joe, that's what I was missing here, so I get now where you're coming from.  I've dodged the issue by using interfaces with explicit implementations (which using Read / Load property, or go against the backing fields) and keeping things internal, and if I need to share rules I include the rule code and interface as a linked file.  Probably not the best way, but it explains why I didn't see the problem. Smile [:)]

RockfordLhotka replied on Thursday, January 07, 2010

JoeFallon1:

This is the point I was making about the re-running of rules. I stated it explicitly in the original thread which the bug ID refers to. When I centralize a rule I have to reflect against the BO and set a Public Property which causes the rules to re-run. A private rule inside a BO can use any of the other techniques to set the backing field and avoid the Property Set.

And I don't plan to change this behavior at all, other than maybe creating a base class like ObjectFactory for building rule classes. If I do that then there'd be a clear way to build public rule methods so they can use BypassPropertyChecks and/or LoadProperty() as necessary.

Peran replied on Monday, January 11, 2010

RockfordLhotka:

other than maybe creating a base class like ObjectFactory for building rule classes.



This 'CommonRulesBase' class sounds like a good idea from a readability point of view.  In my mind 'ObjectFactory' relates to data access a should probably not be included in code targeting the dotNet 4 ClientProfile.

----------------

I have just watched the Core 38 video on business rules and in particular the 'batch mode' processing part using ValidationRules.SuppressRuleChecking to ensure rules are only run once after all properties have been set.

I can see a possible need where in 'batch mode' I would want my mutator ToUpper rules to run, but not my validation rules.  Would the current ideas support this?


Peran

Des Nolan replied on Wednesday, January 06, 2010

Rocky,

making changes mechanical is good

if there automateable with Search and Replace using a helper tool or Regular Expressions that would be even better, as my CSLA application has 250K lines of code

(just signed up to listen to you talk in Boston, and am looking forward to hearing about what's in my future in regards to CSLA -- thanks for the great development framework...this is my second opportunity to see you live, but unlike Vermont, this time around, I won't be riding the Big Yellow Bike from Norwalk there...it's too cold)

Des Nolan

RockfordLhotka replied on Monday, January 11, 2010

I think there's a logic flaw with the idea that "mutator rules should run first".

GIGO: garbage in, garbage out

Simple mutator rules like a ToUpper might need to run before validation. But complex mutator rules like look up the customer details would only run after you've validated the CustomerId value (or whatever).

In other words, the thought that you can always run mutator rules without running validator rules is flawed. I could see a rule set like this:

  1. Required (validator)
  2. ToUpper (mutator)
  3. ValidFormat (validator)
  4. GetCustomerDetails (mutator)

You'd want short-circuiting before doing 4. You'd also want to make sure 2 runs before 3.

ajj3085 replied on Monday, January 11, 2010

What is your thinking on the "run all rules" vs. "run only validation rules (such as just before a dataportal_fetch completes) suggestion?  Would it be possible to do that one with your currently planned implemeation?

RockfordLhotka replied on Monday, January 11, 2010

ajj3085:
What is your thinking on the "run all rules" vs. "run only validation rules (such as just before a dataportal_fetch completes) suggestion?  Would it be possible to do that one with your currently planned implemeation?

You'll note this wasn't in my original scope. I'm entertaining the idea because it has been brought up, but I'm struggling with how it would work in practice.

If you step back and think about this, what CSLA provides is a primitive linear workflow for the rules attached to any given property. Once side-effect of the CSLA 4 changes is that you really could use a WF 4 workflow per property for your rules (or so I hope).

Separating business from validation rules implies two workflows that run one after another. But as I just pointed out, that ignores the very real scenario where business and validation rules are intermixed, and interdependent.

So do you really think it is realistic to just run validation rules, when some mutators are required for validation to work? Or to just run business rules, when you don't know if the data is valid?

If you don't trust the data in your database (hence you want to run validation rules in DP_Fetch), why do you trust its format? Don't you want to run at least your basic mutators on it too?

ajj3085 replied on Monday, January 11, 2010

My thinking was that, using an attribute or something similar, the rule list would still be a single list, and as csla is running through it, it would know (because you asked it to) to simply not run the ones not indicated as validation.

Its a good point that some mutators are required for validators to work, or maybe the other way around, but I think its still important to enable this scenario as well, and I already have a few places where, in the DP_F I have to set a flag, call CheckRules, then disable the flag.  There's another case where it got even more complicated than that, where I had to run all the rules again in certain cases, but there was a mutator rule I couldn't run, so I don't think its always the case that you trust your data, but not its format.

Here's the scenarios I have:

1.  There's a concept of a product bundle; its just a grouping of products into a unit and assigned a part number.  Users can choose to have the bundle calculate its pricing, or set fixed pricing. Going back and forth should calculate or restore the fixed pricing (if it was fixed to begin with).  The way it works is that its not good enough to simply store the original value on fetch, this was causing unexpected behavior.  So I have logic that runs when the pricing type changes, which asks each price to calculate or "uncalculate" which deteremines the apprpriate value to use depending on how the object's state when first loaded.  On each load, I can't have that business rule running, and it should only run in response to a property changed for the price type.

2.  Consider the use case of an "edit account" type scenario.  You may have rules on some properties, some which may only be warnings, which you want to run and should run right out of the database.  But being a good programmer, you create a salt value and hash the password when the user types a new password into the text box.  This ensures the clear-text password never travels over the network.  But again in the fetch, attempting to run all rules will rehash the already hashed password fetched from the database, unless some hackery is put into place. 

Maybe these scenarios aren't worth the effort, but I know I'd find it very useful. I think there is precedent for this too, as Wpf allows seperate validation vs. property changed methods when you create a DependencyProperty.

RockfordLhotka replied on Monday, January 11, 2010

I think a more general solution may be in order.

One of the changes that is part of my core requirements is to alter the method signature for rule methods. It will basically be something along this line:

void RuleMethod(RuleArgs e)

But RuleArgs will now be substantially more sophisticated than it is today, providing a relatively broad set of input and output capabilities.

One thing it could contain is some userstate object that you provide to CheckRules(). This would allow you to use the userstate to do anything you wanted really, in terms of deciding whether a rule should run or not. It would enable your scenario, my scenario and other scenarios - much more flexible than if CSLA hard-codes some specfic scheme and thus precludes the others.

ajj3085 replied on Monday, January 11, 2010

That would work perfectly for me, since my initial suggestion was tacking on a value to CheckRules or AddRule anyway!

Copyright (c) Marimer LLC