/// <summary> /// Runs the enumerable list of rules. /// </summary> /// <param name="rules">The rules.</param> /// <param name="cascade">if set to <c>true</c> cascade.</param> /// <param name="executionContext">The execution context.</param> /// <returns></returns> private RunRulesResult RunRules(IEnumerable <IBusinessRule> rules, bool cascade, RuleContextModes executionContext) { var affectedProperties = new List <string>(); var dirtyProperties = new List <string>(); bool anyRuleBroken = false; foreach (var rule in rules) { // implicit short-circuiting if (anyRuleBroken && rule.Priority > ProcessThroughPriority) { break; } bool complete = false; // set up context var context = new RuleContext((r) => { if (r.Rule.IsAsync) { lock (SyncRoot) { // update output values if (r.OutputPropertyValues != null) { foreach (var item in r.OutputPropertyValues) { // value is changed add to dirtyValues if (((IManageProperties)_target).LoadPropertyMarkDirty(item.Key, item.Value)) { r.AddDirtyProperty(item.Key); } } } // update broken rules list BrokenRules.SetBrokenRules(r.Results, r.OriginPropertyName); // run rules on affected properties for this async rule var affected = new List <string>(); if (cascade) { foreach (var item in r.Rule.AffectedProperties.Distinct()) { if (!ReferenceEquals(r.Rule.PrimaryProperty, item)) { var doCascade = false; if (CascadeOnDirtyProperties && (r.DirtyProperties != null)) { doCascade = r.DirtyProperties.Any(p => p.Name == item.Name); } affected.AddRange(CheckRulesForProperty(item, doCascade, r.ExecuteContext | RuleContextModes.AsAffectedPoperty)); } } } // mark each property as not busy foreach (var item in r.Rule.AffectedProperties) { BusyProperties.Remove(item); _isBusy = BusyProperties.Count > 0; if (!BusyProperties.Contains(item)) { _target.RuleComplete(item); } } foreach (var property in affected.Distinct()) { // property is not in AffectedProperties (already signalled to UI) if (!r.Rule.AffectedProperties.Any(p => p.Name == property)) { _target.RuleComplete(property); } } if (!RunningRules && !RunningAsyncRules) { _target.AllRulesComplete(); } } } else // Rule is Sync { // update output values if (r.OutputPropertyValues != null) { foreach (var item in r.OutputPropertyValues) { // value is changed add to dirtyValues if (((IManageProperties)_target).LoadPropertyMarkDirty(item.Key, item.Value)) { r.AddDirtyProperty(item.Key); } } } // update broken rules list if (r.Results != null) { BrokenRules.SetBrokenRules(r.Results, r.OriginPropertyName); // is any rules here broken with severity Error if (r.Results.Any(p => !p.Success && p.Severity == RuleSeverity.Error)) { anyRuleBroken = true; } } complete = true; } }); context.Rule = rule; if (rule.PrimaryProperty != null) { context.OriginPropertyName = rule.PrimaryProperty.Name; } context.ExecuteContext = executionContext; if (!rule.IsAsync || rule.ProvideTargetWhenAsync) { context.Target = _target; } // get input properties if (rule.InputProperties != null) { var target = (Core.IManageProperties)_target; context.InputPropertyValues = new Dictionary <IPropertyInfo, object>(); foreach (var item in rule.InputProperties) { // do not add lazy loaded fields that have no field data. if ((item.RelationshipType & RelationshipTypes.LazyLoad) == RelationshipTypes.LazyLoad) { if (target.FieldExists(item)) { context.InputPropertyValues.Add(item, target.ReadProperty(item)); } } else { context.InputPropertyValues.Add(item, target.ReadProperty(item)); } } } // mark properties busy if (rule.IsAsync) { lock (SyncRoot) { // mark each property as busy foreach (var item in rule.AffectedProperties) { var alreadyBusy = BusyProperties.Contains(item); BusyProperties.Add(item); _isBusy = true; if (!alreadyBusy) { _target.RuleStart(item); } } } } // execute (or start executing) rule try { rule.Execute(context); } catch (Exception ex) { context.AddErrorResult(string.Format("{0}:{1}", rule.RuleName, ex.Message)); if (rule.IsAsync) { context.Complete(); } } if (!rule.IsAsync) { // process results if (!complete) { context.Complete(); } // copy affected property names affectedProperties.AddRange(rule.AffectedProperties.Select(c => c.Name)); // copy output property names if (context.OutputPropertyValues != null) { affectedProperties.AddRange(context.OutputPropertyValues.Select(c => c.Key.Name)); } // copy dirty properties if (context.DirtyProperties != null) { dirtyProperties.AddRange(context.DirtyProperties.Select(c => c.Name)); } if (context.Results != null) { // explicit short-circuiting if (context.Results.Any(r => r.StopProcessing)) { break; } } } } // return any synchronous results return(new RunRulesResult(affectedProperties, dirtyProperties)); }