/// <summary> /// Determines whether this rule can run the specified context mode. /// </summary> /// <param name="rule">The rule.</param> /// <param name="contextMode">The context mode.</param> /// <returns> /// <c>true</c> if this instance [can run rule] the specified context mode; otherwise, <c>false</c>. /// </returns> internal static bool CanRunRule(IBusinessRule rule, RuleContextModes contextMode) { // default then just return true if (rule.RunMode == RunModes.Default) { return(true); } bool canRun = true; if ((contextMode & RuleContextModes.AsAffectedPoperty) > 0) { canRun = canRun & (rule.RunMode & RunModes.DenyAsAffectedProperty) == 0; } if ((rule.RunMode & RunModes.DenyOnServerSidePortal) > 0) { canRun = canRun & ApplicationContext.LogicalExecutionLocation != ApplicationContext.LogicalExecutionLocations.Server; } if ((contextMode & RuleContextModes.CheckRules) > 0) { canRun = canRun & (rule.RunMode & RunModes.DenyCheckRules) == 0; } return(canRun); }
internal RuleContext(Action <RuleContext> completeHandler, LazySingleton <Dictionary <IPropertyInfo, object> > outputPropertyValues, LazySingleton <List <IPropertyInfo> > dirtyProperties, RuleContextModes executeContext) { _completeHandler = completeHandler; _outputPropertyValues = outputPropertyValues; _dirtyProperties = dirtyProperties; ExecuteContext = executeContext; }
private List <string> CheckRules(Csla.Core.IPropertyInfo property, RuleContextModes executionContext) { if (property == null) { throw new ArgumentNullException("property"); } if (_suppressRuleChecking) { return(new List <string>()); } var oldRR = RunningRules; RunningRules = true; var affectedProperties = new List <string>(); affectedProperties.AddRange(CheckRulesForProperty(property, true, executionContext)); RunningRules = oldRR; if (!RunningRules && !RunningAsyncRules) { _target.AllRulesComplete(); } return(affectedProperties.Distinct().ToList()); }
internal RuleContext(ApplicationContext applicationContext, Action <IRuleContext> completeHandler, LazySingleton <Dictionary <IPropertyInfo, object> > outputPropertyValues, LazySingleton <List <IPropertyInfo> > dirtyProperties, RuleContextModes executeContext) { ApplicationContext = applicationContext ?? throw new ArgumentNullException(nameof(applicationContext)); ExecuteContext = executeContext; _completeHandler = completeHandler; _outputPropertyValues = outputPropertyValues; _dirtyProperties = dirtyProperties; }
/// <summary> /// Invokes all rules attached at the class level /// of the business type. /// </summary> /// <returns> /// Returns a list of property names affected by the invoked rules. /// The PropertyChanged event should be raised for each affected /// property. /// </returns> private List <string> CheckObjectRules(RuleContextModes executionContext, bool cascade) { if (_suppressRuleChecking) { return(new List <string>()); } var oldRR = RunningRules; RunningRules = true; var rules = from r in TypeRules.Rules where r.PrimaryProperty == null && CanRunRule(r, executionContext) orderby r.Priority select r; BrokenRules.ClearRules(null); // Changed to cascade propertyrule to make async ObjectLevel rules rerun PropertLevel rules. var firstResult = RunRules(rules, false, executionContext); // rerun property level rules for affected properties if (cascade) { var propertiesToRun = new List <Csla.Core.IPropertyInfo>(); foreach (var item in rules) { if (!item.IsAsync) { foreach (var p in item.AffectedProperties) { propertiesToRun.Add(p); } } } // run rules for affected properties foreach (var item in propertiesToRun.Distinct()) { var doCascade = false; if (CascadeOnDirtyProperties) { doCascade = firstResult.DirtyProperties.Any(p => p == item.Name); } firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade, executionContext | RuleContextModes.AsAffectedPoperty)); } } RunningRules = oldRR; if (!RunningRules && !RunningAsyncRules) { _target.AllRulesComplete(); } return(firstResult.AffectedProperties.Distinct().ToList()); }
/// <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)); }
/// <summary> /// Invokes all rules for a specific property. /// </summary> /// <param name="property">The property.</param> /// <param name="cascade">if set to <c>true</c> [cascade].</param> /// <param name="executionContext">The execute context.</param> /// <returns></returns> private List <string> CheckRulesForProperty(Csla.Core.IPropertyInfo property, bool cascade, RuleContextModes executionContext) { var rules = from r in TypeRules.Rules where ReferenceEquals(r.PrimaryProperty, property) && CanRunRule(r, executionContext) orderby r.Priority select r; BrokenRules.ClearRules(property); var firstResult = RunRules(rules, cascade, executionContext); if (CascadeOnDirtyProperties) { cascade = cascade || firstResult.DirtyProperties.Any(); } if (cascade) { // get properties affected by all rules var propertiesToRun = new List <Csla.Core.IPropertyInfo>(); foreach (var item in rules) { if (!item.IsAsync) { foreach (var p in item.AffectedProperties) { if (!ReferenceEquals(property, p)) { propertiesToRun.Add(p); } } } } // add PrimaryProperty where property is in InputProperties var input = from r in TypeRules.Rules where !ReferenceEquals(r.PrimaryProperty, property) && r.PrimaryProperty != null && r.InputProperties != null && r.InputProperties.Contains(property) select r.PrimaryProperty; foreach (var p in input) { if (!ReferenceEquals(property, p)) { propertiesToRun.Add(p); } } // run rules for affected properties foreach (var item in propertiesToRun.Distinct()) { var doCascade = false; if (CascadeOnDirtyProperties) { doCascade = firstResult.DirtyProperties.Any(p => p == item.Name); } firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade, executionContext | RuleContextModes.AsAffectedPoperty)); } } // always make sure to add PrimaryProperty firstResult.AffectedProperties.Add(property.Name); return(firstResult.AffectedProperties.Distinct().ToList()); }
/// <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); }
/// <summary> /// Invokes all rules for a specific property. /// </summary> /// <param name="property">The property.</param> /// <param name="cascade">if set to <c>true</c> [cascade].</param> /// <param name="executionContext">The execute context.</param> /// <returns></returns> private List<string> CheckRulesForProperty(Csla.Core.IPropertyInfo property, bool cascade, RuleContextModes executionContext) { var rules = from r in TypeRules.Rules where ReferenceEquals(r.PrimaryProperty, property) && CanRunRule(r, executionContext) orderby r.Priority select r; BrokenRules.ClearRules(property); var firstResult = RunRules(rules, cascade, executionContext); if (CascadeOnDirtyProperties) cascade = cascade || firstResult.DirtyProperties.Any(); if (cascade) { // get properties affected by all rules var propertiesToRun = new List<Csla.Core.IPropertyInfo>(); foreach (var item in rules) if (!item.IsAsync) { foreach (var p in item.AffectedProperties) if (!ReferenceEquals(property, p)) propertiesToRun.Add(p); } // add PrimaryProperty where property is in InputProperties var input = from r in TypeRules.Rules where !ReferenceEquals(r.PrimaryProperty, property) && r.PrimaryProperty != null && r.InputProperties != null && r.InputProperties.Contains(property) select r.PrimaryProperty; foreach (var p in input) { if (!ReferenceEquals(property, p)) propertiesToRun.Add(p); } // run rules for affected properties foreach (var item in propertiesToRun.Distinct()) { var doCascade = false; if (CascadeOnDirtyProperties) doCascade = firstResult.DirtyProperties.Any(p => p == item.Name); firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade, executionContext | RuleContextModes.AsAffectedPoperty)); } } // always make sure to add PrimaryProperty firstResult.AffectedProperties.Add(property.Name); return firstResult.AffectedProperties.Distinct().ToList(); }
/// <summary> /// Determines whether this rule can run the specified context mode. /// </summary> /// <param name="rule">The rule.</param> /// <param name="contextMode">The context mode.</param> /// <returns> /// <c>true</c> if this instance [can run rule] the specified context mode; otherwise, <c>false</c>. /// </returns> internal static bool CanRunRule(IBusinessRule rule, RuleContextModes contextMode) { // default then just return true if (rule.RunMode == RunModes.Default) return true; bool canRun = true; if ((contextMode & RuleContextModes.AsAffectedPoperty) > 0) canRun = canRun & (rule.RunMode & RunModes.DenyAsAffectedProperty) == 0; if ((rule.RunMode & RunModes.DenyOnServerSidePortal) > 0) canRun = canRun & ApplicationContext.LogicalExecutionLocation != ApplicationContext.LogicalExecutionLocations.Server; if ((contextMode & RuleContextModes.CheckRules) > 0) canRun = canRun & (rule.RunMode & RunModes.DenyCheckRules) == 0; return canRun; }
private List<string> CheckRules(Csla.Core.IPropertyInfo property, RuleContextModes executionContext) { if (property == null) throw new ArgumentNullException("property"); if (_suppressRuleChecking) return new List<string>(); var oldRR = RunningRules; RunningRules = true; var affectedProperties = new List<string>(); affectedProperties.AddRange(CheckRulesForProperty(property, true, executionContext)); RunningRules = oldRR; if (!RunningRules && !RunningAsyncRules) _target.AllRulesComplete(); return affectedProperties.Distinct().ToList(); }
/// <summary> /// Invokes all rules attached at the class level /// of the business type. /// </summary> /// <returns> /// Returns a list of property names affected by the invoked rules. /// The PropertyChanged event should be raised for each affected /// property. /// </returns> private List<string> CheckObjectRules(RuleContextModes executionContext, bool cascade) { if (_suppressRuleChecking) return new List<string>(); var oldRR = RunningRules; RunningRules = true; var rules = from r in TypeRules.Rules where r.PrimaryProperty == null && CanRunRule(r, executionContext) orderby r.Priority select r; BrokenRules.ClearRules(null); // Changed to cascade propertyrule to make async ObjectLevel rules rerun PropertLevel rules. var firstResult = RunRules(rules, false, executionContext); // rerun property level rules for affected properties if (cascade) { var propertiesToRun = new List<Csla.Core.IPropertyInfo>(); foreach (var item in rules) if (!item.IsAsync) { foreach (var p in item.AffectedProperties) propertiesToRun.Add(p); } // run rules for affected properties foreach (var item in propertiesToRun.Distinct()) { var doCascade = false; if (CascadeOnDirtyProperties) doCascade = firstResult.DirtyProperties.Any(p => p == item.Name); firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade, executionContext | RuleContextModes.AsAffectedPoperty)); } } RunningRules = oldRR; if (!RunningRules && !RunningAsyncRules) _target.AllRulesComplete(); return firstResult.AffectedProperties.Distinct().ToList(); }
internal RuleContext(Action <RuleContext> completeHandler, RuleContextModes executeContext) : this(completeHandler) { ExecuteContext = executeContext; }
internal RuleContext(ApplicationContext applicationContext, Action <IRuleContext> completeHandler, RuleContextModes executeContext) : this(applicationContext, completeHandler) { ApplicationContext = applicationContext; ExecuteContext = executeContext; }