Missing RuleComplete call in BusinessRules.cs

Missing RuleComplete call in BusinessRules.cs

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


Charleh posted on Tuesday, March 22, 2011

Hi Rocky,

I did post this before in another thread but I think you may have missed it

In CSLA 4.1.0 there seems to be an issue with affected properties and AddOutputPropertyValue. When adding output property values, the propertychanged notification is never raised during sync rules (BusinessRules.cs).

Adding the following line in the rule completed handler in RunRules seems to fix the issue

if (r.OutputPropertyValues != null)
  foreach (var item in r.OutputPropertyValues)
  {
    ((IManageProperties)_target).LoadProperty(item.Key, item.Value);
    // Line below added
    _target.RuleComplete(item.Key);
  }

Cheers,

Charleh

JonnyBee replied on Tuesday, March 22, 2011

So you are calling BusinessRules.CheckRules() on an object that has active databinding to the UI ?

When an object has active databinding rules are typically triggered in the PropertyHasChanged handler i Csla.Core.BusinessBase:

    protected virtual void PropertyHasChanged(Csla.Core.IPropertyInfo property)
    {
      MarkDirty(true);
      var propertyNames = BusinessRules.CheckRules(property);
      if (ApplicationContext.PropertyChangedMode == ApplicationContext.PropertyChangedModes.Windows)
        OnPropertyChanged(property);
      else
        foreach (var name in propertyNames)
          OnPropertyChanged(name);
    }

and this one handles the notification of the UI.

BusinessRules.CheckRules is typically called _before_ the object gets databound and raising PropertyHasChanged is expensive and may make the system even slower when loading large datasets of editable objects.

I rather create a new method in your BusinessBase for CheckAllRules - like this:

    protected void CheckAllRules()
    {
      var propertyNames = BusinessRules.CheckRules();
      if (ApplicationContext.PropertyChangedMode == ApplicationContext.PropertyChangedModes.Windows)
        OnPropertyChanged(property);
      else
        foreach (var name in propertyNames)
          OnPropertyChanged(name);
    }

 

 

 

Charleh replied on Tuesday, March 22, 2011

Hi Jonny, thanks for this, it seems a simple enough solution to retrieve the change list from CheckRules, but it seems that it will still fire a propertychanged for all affected properties regardless if they were output properties or not.

If I add 20 properties to the affected properties list, the rules are run for these properties - if only one of these rules results in an output property then the rule will only raise one property changed notification if I call CheckRules

Does this ring true?

JonnyBee replied on Tuesday, March 22, 2011

No,

Cause PropertyChanged and BusyChanged events are also used to trigger update of ErrorWarnInfo messages in the UI by both PropertyStatus and PropertyInfo (Xaml) and ErrorProvider / ErrorWarnInfoProvider (WindowsForms).

So these events are not ONLY for updating the UI with a new value but also update the property status/error messages in the UI (the result of the rules that ran)  even if the property value itself is unchanged.

Charleh replied on Wednesday, March 23, 2011

So what's the best method of ensuring that rules on children are run when properties on the parent are changed which may affect children?

Take for instance this pseudo BO structure:

(Diners) -> (Meal Dates + Dining Information)

Imagine there are always xx guests in Guest Information, and the Dining Information contains their meal preferences for each meal -

(parent) Diners:

Number of diners

(multiple children) Meal Info:

Meal date + location

Number of people on Menu 1

Number of people on Menu 2

Now imagine a business rule added to the child which says that the NumPeople1 + NumPeople2 must equal the total number of diners on the parent BO

If you change the number of people on the parent, the business rules should be checked on the child to ensure that there are no broken rules, otherwise the user could save without giving a preference for each diner. In the past I've used foreach (child) { child.CheckRules(propertyname) } in the parent PropertyChanged event (when an interesting property changes) - but because there is no change of property on the child, no UI update happens for the children, and on child information screens the validation errors are not displayed

The only method I can think that will cause a UI update for the child is if the parent modifies a property on the child - but this would mean creating an extra field on the children to hold the total number of diners and updating that from the parent. This could also mean that the child is potentially dirty even though nothing had changed, because an actual property was modified on the object

Are there any good examples of this working in the CSLA demos?

JonnyBee replied on Wednesday, March 23, 2011

Hi,

In this use case I'd prefer to have:

a) a field on diners for "Number of diners"
b) a calculated field (in OnChildChanged on Diners) for "Total number of people on menus"

And a rule on Diners.NumberOfDiners that says these 2 must be equal with a Dependency from "Total number of people on menus" to "NumberOfDiners".

I wouldn't put an error message on each "Meal Info". Entering or altering the number of people would trigger an update in "Total" and the rule on NumberOfDiners.

And as long as there is a broken rule in the object graph then the graph is not savable.

Charleh replied on Wednesday, March 23, 2011

Just to clarify the requirement:

Diners 
Total: 2 people eating at each meal

Meal 1
Menu1: 1
Menu2: 1  
RECORD IS OK

Meal 2
Menu1: 2
Menu2: 0  
RECORD IS OK

Meal 3
Menu1: 1
Menu2: 0  
RECORD IS INVALID

Meal 4
Menu1: 0
Menu2: 0  
RECORD IS INVALID

So if the error happened on the parent, the parent would need to loop through each child object and sum the Menu1 + Menu2 field - then if the value didn't match the Total a broken rule would be added. This would work and would prevent the object being saved - but it would not be very intuitive to the user. Imagine they had over 1000 records (ok meals are probably a bad example case for this) - in this situation the user would not know which object the error occurred on. 

I could potentially add information to the parent broken rule to tell which object had the error, but if more than 1 child object has an error this broken rule becomes quite a large string and has no logical separation of the error objects - how does the user know which one to correct?

I have created an error display that uses a tree view to notify the user of where the errors occurred, and it works well, but if I use the method you suggested, this becomes less useful and less user-friendly

Also, talking about the databinding - if the initial CheckRules happens on the server before any databinding - how does raising PropertyChanged affect performance so negatively? If there are no subscribers to the event, the only overhead you have is the check for subscribers - no additional code is executed

Additional:

Even if I'm calling child.CheckRules(IPropertyInfo) on a child object - not child.CheckRules() - this does not update the UI when output properties are added. In my opinion this should raise a propertychanged for the primary property (which it does) and all output values (which it doesn't)

Copyright (c) Marimer LLC