Does WaitForIdle take chained Async rules into account? #4230
-
I'm having an issue where I'm recursively running all of my business rules on my BO and all of it's children and grandchildren, and then displaying those broken rules. My problem is, is that not all of the rules are being displayed. In tracing things out, it appears that the async rules are still running at the point that I'm displaying my broken rules. I have found the WaitForIdle method in hopes that it will wait for all of the async rules to come back. However, that's not the case. I have a set of chained async rules in a grandchild that have not returned yet when the broken rules are displayed. If I add a Task.Delay for even just a millisecond, then all of the broken rules display properly. So my question is whether WaitForIdle waits for chained rules to return as well. Here is a code sample of what's running vm.Model.CloseDate = vm.Model.ProposedCloseDate; // setting this property triggers a bunch of validation rules
await vm.Model.WaitForIdle(); // This appears to work after the the CloseDate was changed
await vm.Model.ValidateObjectAsync(true); // Now validate the entire object and all descendents
await vm.Model.WaitForIdle(); // This wait for idle isn't working as expected
await Task.Delay(1); // Adding the delay will display all broken rules, but if omitted, brokenRules below is incomplete
brokenRules = Csla.Rules.BusinessRules.GetAllBrokenRules(vm.Model, false); Here is what ValidateObjectAsync looks like: public async Task ValidateObjectAsync(bool IncludeChildren)
{
if (IncludeChildren)
{
await RecursiveValidateObjectAsync();
}
else
{
await ValidateObjectAsync();
}
}
private async Task RecursiveValidateObjectAsync()
{
await ValidateObjectAsync();
// Get the children
foreach (object item in FieldManager.GetChildren())
{
if (item is INetsoftEditableCollection itemCollectionBO)
{
await itemCollectionBO.ValidateCollectionAsync(true);
}
else if (item is INetsoftEditableBusinessObject itemBO)
{
await itemBO.ValidateObjectAsync();
}
}
}
public async Task ValidateObjectAsync()
{
await BusinessRules.CheckRulesAsync();
}
public async Task ValidateCollectionAsync(bool IncludeChildren)
{
foreach (dynamic item in this)
await item.ValidateObjectAsync(IncludeChildren);
} And I'm calling a few chained rules like this: public IsFieldAssociatedGatewayAsync(IPropertyInfo primaryProp, IPropertyInfo associationTypeIDProperty, IPropertyInfo uDFValueHeaderIDProperty,
IPropertyInfo advFundIDsProperty, IPropertyInfo advProjectTypeIDProperty, IPropertyInfo uDFFieldIDProperty,
IPropertyInfo referenceDateProperty, IBusinessRuleBase innerRule) : base(primaryProp)
{
AssociationTypeIDProperty = associationTypeIDProperty;
UDFValueHeaderIDProperty = uDFValueHeaderIDProperty;
AdvFundIDsProperty = advFundIDsProperty;
AdvProjectTypeIDProperty = advProjectTypeIDProperty;
UDFFieldIDProperty = uDFFieldIDProperty;
ReferenceDateProperty = referenceDateProperty;
InnerRule = innerRule;
InputProperties = new List<IPropertyInfo>() { PrimaryProperty, AssociationTypeIDProperty, UDFValueHeaderIDProperty,
AdvFundIDsProperty, AdvProjectTypeIDProperty, UDFFieldIDProperty,
ReferenceDateProperty };
if (InnerRule.InputProperties != null)
InputProperties.AddRange(InnerRule.InputProperties);
InputProperties = new List<IPropertyInfo>(InputProperties.Distinct());
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CSLA0017:Find Business Rules That Do Not Use Add() Methods on the Context", Justification = "<Pending>")]
protected override async Task ExecuteAsync(IRuleContext context)
{
AssociationTypes AssociationTypeID = context.GetInputValue<AssociationTypes>(AssociationTypeIDProperty);
int udfValueHeaderID = context.GetInputValue<int>(UDFValueHeaderIDProperty);
string advFundIDs = context.GetInputValue<string>(AdvFundIDsProperty);
int advProjectTypeID = context.GetInputValue<int>(AdvProjectTypeIDProperty);
int uDFFieldID = context.GetInputValue<int>(UDFFieldIDProperty);
SmartDate referenceDate = context.GetInputValue<SmartDate>(ReferenceDateProperty);
UDFValues values = context.GetInputValue<UDFValues>(PrimaryProperty);
bool retVal = true;
if (AssociationTypeID == AssociationTypes.None)
{
retVal = false;
}
else
{
var portal = context.ApplicationContext.GetRequiredService<IDataPortal<UDFAssociatedCommand>>();
UDFAssociatedCommand cmd = await portal.CreateAsync(uDFFieldID, advFundIDs, advProjectTypeID,referenceDate, context.ApplicationContext.GetUserTimeZone());
cmd = await portal.ExecuteAsync(cmd);
if (cmd is not null && !cmd.IsAssociated)
{
retVal = false;
}
}
if (retVal)
{
context.ExecuteRule(InnerRule);
}
}
} Just to recap, WaitForIdle isn't actually waiting for all of the rules to finish running. I'm speculating that it may have something to do with it not taking chained rules into account, but if anyone has any ideas what could be happening, it would be greatly appreciated. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 5 replies
-
Hi. To be specific What kind of rule is executed with the snippet if (retVal)
{
context.ExecuteRule(InnerRule);
} In your rule? I don't know how the rule engine behaves if you execute an inner - async - rule without awaiting it. I suspect - currently - the busy state is reseted early because the rule engine thinks the rule execution is finished. But that's just a speculation. Revisitng your text you put another async rule as inner rule in that rule. So you should make sure the inner rule execution is properly awaited in the rules context. As stated the rule engine probably thinks the rule execution is finished and proceeds instead of waiting for a "hidden" one. HTH |
Beta Was this translation helpful? Give feedback.
We execute an inner async rule this way:
await _innerRule.ExecuteAsync(context.GetChainedContext(_innerRule));
And
_innerRule
is of typeIBusinessRuleAsync
.