Ui is not being updated for this rule (everything else works okay)
private class LookISBN : PropertyRule// Csla.Rules.BusinessRule { private readonly IPropertyInfo _schoolProperty; private readonly IPropertyInfo _authorProperty; public LookISBN(IPropertyInfo primaryProperty, IPropertyInfo schoolProperty, params IPropertyInfo[] affectedProperties) : base(primaryProperty) { IsAsync = true; _schoolProperty = schoolProperty; InputProperties = new List<IPropertyInfo>(); InputProperties.Add(PrimaryProperty); InputProperties.Add(schoolProperty); AffectedProperties.AddRange(affectedProperties); _authorProperty = AffectedProperties[1]; CanRunAsAffectedProperty = false; CanRunInCheckRules = false; CanRunOnServer = false; } protected override void Execute(RuleContext context) { if (context.InputPropertyValues[PrimaryProperty] != null) { string isbn = context.InputPropertyValues[PrimaryProperty].ToString(); if (!string.IsNullOrWhiteSpace(isbn)) { var schoolid = context.InputPropertyValues[_schoolProperty].ToString(); AddStockInfo.GetAddStockInfo(isbn, schoolid, "A", (o, e) => { if (e.Object != null && e.Error == null) {
//e.Object.Author is populated okay
context.AddOutValue(_authorProperty, e.Object.Author); context.AddOutValue(AffectedProperties[2], e.Object.Title); context.AddOutValue(AffectedProperties[3], e.Object.SubjectIndexID); } context.Complete(); }); //context.AddOutValue(AffectedProperties[1], "author"); //context.AddOutValue(AffectedProperties[2], "title"); //context.AddOutValue(AffectedProperties[3], 112); } } } }
Thanks for your help
And this is how I call it
BusinessRules.AddRule(new LookISBN(ISBN13Property, SchoolIDProperty, AuthorProperty, TitleProperty, SubjectIndexIDProperty));
You must ALWAYS MAKE SURE THAT ALL EXCEUTION FLOWS CALL Context.Complete in an async rules Execute method.
IE:
if (context.InputPropertyValues[PrimaryProperty] != null) { string isbn = context.InputPropertyValues[PrimaryProperty].ToString(); if (!string.IsNullOrWhiteSpace(isbn)) {
NONE of these if statements make sure to call Context.Complete and this is going to mess up the rule engine.
Meaning that when you do call async method - always make sure to call Context.Complete() (and your rule does)
Also - whenever your rule decides to NOT do an async method you MUST call Context.Complete() in order to let the rule engine know that it has completed.
This the actual code in BusinessRules.RunRules:
foreach (var item in r.Rule.AffectedProperties)
{
BusyProperties.Remove(item);
_isBusy = BusyProperties.Count > 0;
if (!BusyProperties.Contains(item))
_target.RuleComplete(item);
}
and in BusinessBase
void IHostRules.RuleComplete(IPropertyInfo property)
{
OnPropertyChanged(property);
OnBusyChanged(new BusyChangedEventArgs(property.Name, false));
MetaPropertyHasChanged("IsSelfValid");
MetaPropertyHasChanged("IsValid");
MetaPropertyHasChanged("IsSavable");
}
Giving that ONLY WHEN THE PROPERTY IS NO LONGER BUSY - Then the RuleComnplete handler will call target.RuleComplet to notify UI.
So when the rule fails to call Context.Complete the properties will always remain busy and no notification will be given.
FWIW: I would never us AffectedProperties like you do here. If they must be in a specific order then I would prefer to have these as separate parameters to the rule constructor. No-one will ever be able to understand the sequence from that constructors signature.
Thanks - it did actually start working all of a sudden - however I will heed your good advice.
Is this what you mean [see bold above] (FWIW I would not use AffectedProperties....)?
private class LookISBN : PropertyRule// Csla.Rules.BusinessRule { private readonly IPropertyInfo _schoolProperty; private readonly IPropertyInfo _prop1;private readonly IPropertyInfo _prop2;public LookISBN(IPropertyInfo primaryProperty, IPropertyInfo schoolProperty, IPropertyInfo prop1, IPropertyInfo prop2, etc )
: base(primaryProperty) { IsAsync = true; _schoolProperty = schoolProperty;
_prop1 = prop1;
_prop2 = prop2;InputProperties = new List<IPropertyInfo>(); InputProperties.Add(PrimaryProperty); InputProperties.Add(schoolProperty); Affectedproperties.Add( prop1);Affectedproperties.Add( prop2);CanRunAsAffectedProperty = false; CanRunInCheckRules = false; CanRunOnServer = false; } protected override void Execute(RuleContext context) { if (context.InputPropertyValues[PrimaryProperty] != null) { string isbn = context.InputPropertyValues[PrimaryProperty].ToString(); if (!string.IsNullOrWhiteSpace(isbn)) { var schoolid = context.InputPropertyValues[_schoolProperty].ToString(); AddStockInfo.GetAddStockInfo(isbn, schoolid, "A", (o, e) => { if (e.Object != null && e.Error == null) { context.AddOutValue(_prop1, e.Object.Author); context.AddOutValue(_prop2, e.Object.Title); } context.Complete(); }); } } } }
I'd probably use strong typed parameters with proper names like:
public LookISBN(IPropertyInfo primaryProperty, PropertyInfo<string> schoolProperty,
PropertInfo<string> authorProperty, PropertyInfo<int> subjectIndexIdProperty)
This signature is quite self explaining.
And your coding style in Execute should be like:
if (context.InputPropertyValues[PrimaryProperty] == null) {
context.Complete();
return;
}
string isbn = context.InputPropertyValues[PrimaryProperty].ToString(); if (string.IsNullOrWhiteSpace(isbn)) {
context.Complete();
return;
}
// then do the async operation
Thanks Jonny
Copyright (c) Marimer LLC