Hi,
I have an object "Bar" that is a root object.
I've added a Business rule on this object (Property
rule).
The rule is really CPU intensive and need to be run only
when the user asks to validate the object so I've implemented the property like
this:
public DateTime Start
{
get { return
GetProperty(StartProperty); }
set
{
((ICheckRules)this).SuppressRuleChecking();
SetProperty(StartProperty, value.Date);
((ICheckRules)this).ResumeRuleChecking();
}
}
With this mechanism in place, the rule is not called
every time the property changed.
So calling BusinessRules.GetAllBrokenRules(this,
true) return an empty list of BrokenRules.
Now if I want to validate this object I need to manually
call ((ICheckRules)this).CheckRules().
That is fine!
My problem come when I want to use The "Bar"
object as an item (Child) of a Collection that is a child of another object.
For example if I have a "Foo" object that
contains a BusinessList of "Bar" objects.
Calling BusinessRules.GetAllBrokenRules(fooObject,
true) with an invalid Bar item on a "Foo" object BEFORE running ((ICheckRules)subBarObject).CheckRules() on all BarObject creates
an empty list of BrokenRules.
To correct this behavior I need to Call the
"CheckRules" method on all children.
After these call are made, the call to BusinessRules.GetAllBrokenRules(fooObject,
true) return the errors contained in the children objects.
My interrogation is that I'm not sure if:
On my side I should override the ICheckRules.CheckRule method on all root objects to call "CheckRules"
on the items of child list
OR
Csla.BusinessBase class should do it for me.
Especially if I have registered the BusinessList property
as Child (RelationshipTypes.Child)?
To understand what’s happening take a look at the
attached project.
Start the app and follow the steps.
Thanks a lot
This is the same as:
public DateTime Start
{
get { return
GetProperty(StartProperty); }
set { LoadProperty(StartProperty, value.Date); }
}
My preference is to make "expensive rule" into async rules.
The primary goal of Business Rules is to
So the expensive rule should
Imagine a list of N Bar items and you execute all rules for each bar - no matter if the user has made an edit to one, two or all items? That would really slow down your app, wouldn't it?
So I would consider to add an CPUIntensiveCommand with async dataportal call:
[Serializable] public class CPUIntensiveCommand : CommandBase<CPUIntensiveCommand> { //public static readonly PropertyInfo<string> Param1Property = RegisterProperty<string>(c => c.Param1); //public string Param1 //{ // get { return ReadProperty(Param1Property); } // private set { LoadProperty(Param1Property, value); } //} public static readonly PropertyInfo<int> ResultProperty = RegisterProperty<int>(c => c.Result); public int Result { get { return ReadProperty(ResultProperty); } private set { LoadProperty(ResultProperty, value); } } #region Factory Methods public static void BeginExecute(string param1, object userState, EventHandler<DataPortalResult<CPUIntensiveCommand>> callback) { var cmd = new CPUIntensiveCommand(); // cmd.Param = param1; DataPortal.BeginExecute<CPUIntensiveCommand>(cmd, callback, userState); } public static CPUIntensiveCommand Execute(string param1) { CPUIntensiveCommand cmd = new CPUIntensiveCommand(); // cmd.Param = param1; return DataPortal.Execute<CPUIntensiveCommand>(cmd); } public CPUIntensiveCommand() { /* require use of factory methods */ } #endregion #region Server-side Code private static int count = 0; protected void DataPortal_Execute() { Thread.Sleep(2500); count++; Result = count; } #endregion }
and make the rule run asyncronously
public class CPUIntensiveRule : PropertyRule { public CPUIntensiveRule(IPropertyInfo primaryProperty, string errorMessage) : base(primaryProperty) { MessageText = errorMessage; CanRunAsAffectedProperty = false; CanRunInCheckRules = false; IsAsync = true; } protected override void Execute(RuleContext context) { CPUIntensiveCommand.BeginExecute(string.Empty, context, Complete); } private void Complete(object sender, DataPortalResult<CPUIntensiveCommand> e) { var context = (RuleContext) e.UserState; if (e.Object.Result % 3 == 1) context.AddErrorResult(PrimaryProperty, GetMessage());
context.Complete();
} }
And you should also look at Csla.Xaml.ViewModelBase to show an indicator to the user that
the object is "busy" and get "bindable" meta properties (CanSave, IsDirty etc).
Copyright (c) Marimer LLC