CSLA .NET

Vibrant discussion about CSLA .NET and using the framework to build great business applications.

Who should execute Child Object Business Rules

rated by 0 users
Not Answered This post has 0 verified answers | 1 Reply | 1 Follower

Not Ranked
1 Posts

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

All Replies

Top 10 Contributor
1,772 Posts

MarcBoissonneault:
public DateTime Start
{
       get { return GetProperty(StartProperty); }
       set
       {
                ((ICheckRules)this).SuppressRuleChecking();
                SetProperty(StartProperty, value.Date);
                ((ICheckRules)this).ResumeRuleChecking();
        }
}

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

  • give user feedback on errors as soon as possible after edit
  • only execute relevant rules for the edit made

So the expensive rule should

  • run asyncronously on the client (CanRunInCheckRules = false, CanRunAsAffectedProperty = false)
  • if you do NOT trust the data in the database then run expensive rule synchronously in data access)

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).

Jonny Bekkum, Norway CslaContrib Coordinator

Page 1 of 1 (2 items) | RSS

Copyright (c) 2006-2010 Marimer LLC. All rights reserved.
Email admin@lhotka.net for support.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems