9using System.Collections;
10using System.ComponentModel;
14using System.Collections.Generic;
17using System.Collections.ObjectModel;
18using System.Threading.Tasks;
31 private object SyncRoot =
new object();
39 if (_brokenRules ==
null)
45 private bool _suppressRuleChecking;
54 get {
return _suppressRuleChecking; }
55 set { _suppressRuleChecking = value; }
58 private int _processThroughPriority;
65 get {
return _processThroughPriority; }
66 set { _processThroughPriority = value; }
69 private string _ruleSet =
null;
76 get {
return string.IsNullOrEmpty(_ruleSet) ? ApplicationContext.DefaultRuleSet : _ruleSet; }
80 _typeAuthRules =
null;
81 _ruleSet = value == ApplicationContext.DefaultRuleSet ? null : value;
82 if (BrokenRules.Count > 0)
84 BrokenRules.ClearRules();
97 get {
return _cascadeOnDirtyProperties; }
98 set { _cascadeOnDirtyProperties = value; }
108 if (_typeRules ==
null && _target !=
null)
115 private AuthorizationRuleManager _typeAuthRules;
116 internal AuthorizationRuleManager TypeAuthRules
120 if (_typeAuthRules ==
null && _target !=
null)
121 _typeAuthRules = AuthorizationRuleManager.GetRulesForType(_target.GetType(), _ruleSet);
122 return _typeAuthRules;
133 var result =
new List<string>();
134 foreach (var item
in TypeRules.
Rules)
135 result.Add(item.RuleName);
136 return result.ToArray();
148 internal object Target
150 get {
return _target; }
174 TypeRules.
Rules.Add(rule);
185 typeRules.
Rules.Add(rule);
194 EnsureUniqueRule(TypeAuthRules, rule);
195 TypeAuthRules.
Rules.Add(rule);
206 AddRule(objectType, rule, ApplicationContext.RuleSet);
219 EnsureUniqueRule(typeRules, rule);
220 typeRules.Rules.Add(rule);
227 oldRule = mgr.
Rules.FirstOrDefault(c => c.Element !=
null && c.Element.Name == rule.
Element.
Name && c.Action == rule.
Action);
229 oldRule = mgr.
Rules.FirstOrDefault(c => c.Element ==
null && c.Action == rule.
Action);
231 throw new ArgumentException(
"rule");
253 private bool _runningRules;
260 get {
return _runningRules; }
261 private set { _runningRules = value; }
265 private bool _isBusy;
273 if (_busyChanged ==
null)
285 get {
return _isBusy; }
303 return BusyProperties.Contains(property);
313 objectType = ApplicationContext.DataPortalActivator.ResolveType(objectType);
315 return HasPermission(action,
null, objectType,
null, ApplicationContext.RuleSet);
326 objectType = ApplicationContext.DataPortalActivator.ResolveType(objectType);
328 return HasPermission(action,
null, objectType, criteria, ApplicationContext.RuleSet);
342 return HasPermission(action,
null, objectType,
null, ruleSet);
352 return HasPermission(action, obj, obj.GetType(),
null, ApplicationContext.RuleSet);
366 return HasPermission(action, obj, obj.GetType(),
null, ruleSet);
375 throw new ArgumentOutOfRangeException(nameof(action));
382 var context =
new AuthorizationContext { Rule = rule, Target = obj, TargetType = objType, Criteria = criteria };
383 rule.Execute(context);
384 result = context.HasPermission;
396 if (_suppressRuleChecking)
403 throw new ArgumentOutOfRangeException(nameof(action));
407 TypeAuthRules.
Rules.FirstOrDefault(c => c.Element !=
null && c.Element.Name == element.Name && c.Action == action);
410 var context =
new AuthorizationContext { Rule = rule, Target = this.Target, TargetType = this.Target.GetType() };
411 rule.Execute(context);
412 result = context.HasPermission;
426 if (_suppressRuleChecking)
431 TypeAuthRules.
Rules.FirstOrDefault(c => c.Element !=
null && c.Element.Name == element.Name && c.Action == action);
433 result = rule.CacheResult;
451 var tasks =
new Task[] { BusyChanged.
WaitAsync(), Task.Delay(timeout) };
452 var
final = await Task.WhenAny(tasks);
453 if (
final == tasks[1])
482 if (_suppressRuleChecking)
483 return new List<string>();
487 var properties = TypeRules.
Rules.Where(p => p.PrimaryProperty !=
null)
488 .Select(p => p.PrimaryProperty)
490 foreach (var property
in properties)
495 return affectedProperties.Distinct().ToList();
524 if (_suppressRuleChecking)
525 return new List<string>();
529 var rules = from r in TypeRules.
Rules
530 where r.PrimaryProperty ==
null
531 && CanRunRule(r, executionContext)
534 BrokenRules.ClearRules(
null);
536 var firstResult = RunRules(rules,
false, executionContext);
542 foreach (var item
in rules)
545 foreach (var p
in item.AffectedProperties)
546 propertiesToRun.Add(p);
549 foreach (var item
in propertiesToRun.Distinct())
551 var doCascade =
false;
553 doCascade = firstResult.DirtyProperties.Any(p => p == item.Name);
554 firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade,
562 return firstResult.AffectedProperties.Distinct().ToList();
582 if (property ==
null)
583 throw new ArgumentNullException(
"property");
585 if (_suppressRuleChecking)
586 return new List<string>();
591 var affectedProperties =
new List<string>();
592 affectedProperties.AddRange(CheckRulesForProperty(property,
true, executionContext));
597 return affectedProperties.Distinct().ToList();
608 internal static bool CanRunRule(IBusinessRuleBase rule,
RuleContextModes contextMode)
611 if (rule.RunMode ==
RunModes.Default)
return true;
616 canRun &= (rule.RunMode &
RunModes.DenyAsAffectedProperty) == 0;
618 if ((rule.RunMode &
RunModes.DenyOnServerSidePortal) > 0)
619 canRun &= ApplicationContext.LogicalExecutionLocation != ApplicationContext.LogicalExecutionLocations.Server;
622 canRun &= (rule.RunMode &
RunModes.DenyCheckRules) == 0;
636 var rules = from r in TypeRules.
Rules
637 where ReferenceEquals(r.PrimaryProperty, property)
638 && CanRunRule(r, executionContext)
642 BrokenRules.ClearRules(property);
643 var firstResult = RunRules(rules, cascade, executionContext);
645 cascade = cascade || firstResult.DirtyProperties.Any();
650 foreach (var item
in rules)
653 foreach (var p
in item.AffectedProperties)
654 if (!ReferenceEquals(property, p))
655 propertiesToRun.Add(p);
659 var input = from r in TypeRules.
Rules
660 where !ReferenceEquals(r.PrimaryProperty, property)
661 && r.PrimaryProperty !=
null
662 && r.InputProperties !=
null
663 && r.InputProperties.Contains(property)
664 select r.PrimaryProperty;
666 foreach (var p
in input)
668 if (!ReferenceEquals(property, p))
669 propertiesToRun.Add(p);
672 foreach (var item
in propertiesToRun.Distinct())
674 var doCascade =
false;
676 doCascade = firstResult.DirtyProperties.Any(p => p == item.Name);
677 firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade,
683 firstResult.AffectedProperties.Add(property.Name);
684 return firstResult.AffectedProperties.Distinct().ToList();
690 private bool _cascadeOnDirtyProperties;
696 if (_busyProperties ==
null)
698 return _busyProperties;
709 private RunRulesResult RunRules(IEnumerable<IBusinessRuleBase> rules,
bool cascade,
RuleContextModes executionContext)
711 var affectedProperties =
new List<string>();
712 var dirtyProperties =
new List<string>();
713 bool anyRuleBroken =
false;
714 foreach (var rule
in rules)
719 bool complete =
false;
721 var context =
new RuleContext((r) =>
728 if (r.OutputPropertyValues != null)
729 foreach (var item in r.OutputPropertyValues)
732 if (((IManageProperties)_target).LoadPropertyMarkDirty(item.Key, item.Value))
733 r.AddDirtyProperty(item.Key);
736 BrokenRules.SetBrokenRules(r.Results, r.OriginPropertyName);
739 var affected = new List<string>();
741 foreach (var item in r.Rule.AffectedProperties.Distinct())
742 if (!ReferenceEquals(r.Rule.PrimaryProperty, item))
744 var doCascade = false;
745 if (CascadeOnDirtyProperties && (r.DirtyProperties != null))
746 doCascade = r.DirtyProperties.Any(p => p.Name == item.Name);
747 affected.AddRange(CheckRulesForProperty(item, doCascade, r.ExecuteContext | RuleContextModes.AsAffectedPoperty));
751 foreach (var item in r.Rule.AffectedProperties)
753 BusyProperties.Remove(item);
754 RunningAsyncRules = BusyProperties.Count > 0;
755 if (!BusyProperties.Contains(item))
756 _target.RuleComplete(item);
759 foreach (var property in affected.Distinct())
762 if (!r.Rule.AffectedProperties.Any(p => p.Name == property))
763 _target.RuleComplete(property);
766 if (!RunningRules && !RunningAsyncRules)
767 _target.AllRulesComplete();
773 if (r.OutputPropertyValues !=
null)
774 foreach (var item in r.OutputPropertyValues)
777 if (((IManageProperties)_target).LoadPropertyMarkDirty(item.Key, item.Value))
778 r.AddDirtyProperty(item.Key);
782 if (r.Results !=
null)
784 BrokenRules.SetBrokenRules(r.Results, r.OriginPropertyName);
787 if (r.Results.Any(p => !p.Success && p.Severity == RuleSeverity.Error))
788 anyRuleBroken = true;
797 if (rule.PrimaryProperty !=
null)
798 context.OriginPropertyName = rule.PrimaryProperty.Name;
799 context.ExecuteContext = executionContext;
800 if (!rule.IsAsync || rule.ProvideTargetWhenAsync)
801 context.Target = _target;
804 if (rule.InputProperties !=
null)
806 var target = (IManageProperties) _target;
807 context.InputPropertyValues =
new Dictionary<IPropertyInfo, object>();
808 foreach (var item
in rule.InputProperties)
813 if (target.FieldExists(item))
814 context.InputPropertyValues.Add(item, target.ReadProperty(item));
817 context.InputPropertyValues.Add(item, target.ReadProperty(item));
827 foreach (var item
in rule.AffectedProperties)
829 var alreadyBusy = BusyProperties.Contains(item);
830 BusyProperties.Add(item);
841 if (rule is IBusinessRule syncRule)
842 syncRule.Execute(context);
843 else if (rule is IBusinessRuleAsync asyncRule)
844 RunAsyncRule(asyncRule, context);
846 throw new ArgumentOutOfRangeException(rule.GetType().FullName);
850 context.AddErrorResult(
string.Format(
"{0}:{1}", rule.RuleName, ex.Message));
861 affectedProperties.AddRange(rule.AffectedProperties.Select(c => c.Name));
863 if (context.OutputPropertyValues !=
null)
864 affectedProperties.AddRange(context.OutputPropertyValues.Select(c =>c.Key.Name));
866 if (context.DirtyProperties !=
null)
867 dirtyProperties.AddRange(context.DirtyProperties.Select(c => c.Name));
869 if (context.Results !=
null)
872 if (context.Results.Any(r => r.StopProcessing))
878 return new RunRulesResult(affectedProperties, dirtyProperties);
881 private async
void RunAsyncRule(IBusinessRuleAsync asyncRule, IRuleContext context)
885 await asyncRule.ExecuteAsync(context);
893 #region DataAnnotations
899 [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
903#if !NETSTANDARD2_0 && !NET5_0
905 var classAttList = _target.GetType().GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.MetadataTypeAttribute),
true);
906 if (classAttList.Length > 0)
908 metadataType = ((System.ComponentModel.DataAnnotations.MetadataTypeAttribute)classAttList[0]).MetadataClassType;
909 AddDataAnnotationsFromType(metadataType);
914 metadataType = _target.GetType();
915 AddDataAnnotationsFromType(metadataType);
922 private void AddDataAnnotationsFromType(Type metadataType)
924 var attList = metadataType.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.ValidationAttribute),
true);
925 foreach (var att
in attList)
926 AddRule(
new CommonRules.DataAnnotation(
null, (System.ComponentModel.DataAnnotations.ValidationAttribute)att));
929 var propList = metadataType.GetProperties();
930 foreach (var prop
in propList)
932 attList = prop.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.ValidationAttribute),
true);
933 foreach (var att
in attList)
935 var target = (IManageProperties)_target;
936 var pi = target.GetManagedProperties().First(c => c.Name == prop.Name);
937 AddRule(
new CommonRules.DataAnnotation(pi, (System.ComponentModel.DataAnnotations.ValidationAttribute)att));
944 #region MobileObject overrides
958 info.
AddValue(
"_processThroughPriority", _processThroughPriority);
959 info.
AddValue(
"_ruleSet", _ruleSet);
960 info.
AddValue(
"_cascadeWhenChanged", _cascadeOnDirtyProperties);
961 base.OnGetState(info, mode);
976 _processThroughPriority = info.GetValue<
int>(
"_processThroughPriority");
977 _ruleSet = info.GetValue<
string>(
"_ruleSet");
978 _cascadeOnDirtyProperties = info.GetValue<
bool>(
"_cascadeWhenChanged");
979 base.OnSetState(info, mode);
995 if (_brokenRules !=
null && _brokenRules.Count > 0)
1001 base.OnGetChildren(info, formatter);
1017 if (info.
Children.ContainsKey(
"_brokenRules"))
1019 int referenceId = info.
Children[
"_brokenRules"].ReferenceId;
1023 base.OnSetChildren(info, formatter);
1027 #region Serialization Notification
1031 OnDeserializedHandler(
new System.Runtime.Serialization.StreamingContext());
1034 [System.Runtime.Serialization.OnDeserialized]
1035 private void OnDeserializedHandler(System.Runtime.Serialization.StreamingContext context)
1037 SyncRoot =
new object();
1042 #region Get All Broken Rules (tree)
1051 return GetAllBrokenRules(root,
true);
1063 long childBrokenRuleCount = 0;
1064 AddNodeToBrukenRules(ref list, ref counter,
null, root, errorsOnly, ref childBrokenRuleCount);
1069 private static void AddNodeToBrukenRules(ref
BrokenRulesTree list, ref
long counter,
object parentKey,
object obj,
bool errorsOnly, ref
long childBrokenRuleCount)
1074 var nodeKey = counter++;
1076 long myChildBrokenRuleCount = bo.BrokenRulesCollection.Count;
1077 var node =
new BrokenRulesNode() { Parent = parentKey, Node = nodeKey, BrokenRules = bo.BrokenRulesCollection, Object = obj };
1081 foreach (var child
in ((IManageProperties)bo).GetChildren())
1083 AddNodeToBrukenRules(ref list, ref counter, nodeKey, child, errorsOnly, ref myChildBrokenRuleCount);
1087 if (!errorsOnly && myChildBrokenRuleCount == 0)
1091 if (errorsOnly && bo.IsValid)
1095 childBrokenRuleCount += myChildBrokenRuleCount;
1101 var nodeKey = counter++;
1103 var node =
new BrokenRulesNode() { Parent = parentKey, Node = nodeKey, Object = obj, BrokenRules =
new BrokenRulesCollection(
true) };
1104 long myChildBrokenRuleCount = 0;
1108 foreach (var child
in (IEnumerable)obj)
1110 AddNodeToBrukenRules(ref list, ref counter, nodeKey, child, errorsOnly, ref myChildBrokenRuleCount);
1114 if (!errorsOnly && myChildBrokenRuleCount == 0)
1118 if (errorsOnly && isValid)
1122 childBrokenRuleCount += myChildBrokenRuleCount;
1130 internal class RunRulesResult
1132 public RunRulesResult(List<string> affectedProperties, List<string> dirtyProperties)
1134 AffectedProperties = affectedProperties;
1135 DirtyProperties = dirtyProperties;
1138 public List<string> AffectedProperties {
get;
set; }
1139 public List<string> DirtyProperties {
get;
set; }
1142 object IBusinessRules.Target
1144 get {
return Target; }
This is the non-generic base class from which most business objects will be derived.
Inherit from this base class to easily create a serializable class.
Context information provided to an authorization rule when it is invoked.
Manages the list of authorization rules for a business type.
List< IAuthorizationRule > Rules
Gets the list of rule objects for the business type.
A collection of currently broken rules.
int ErrorCount
Gets the number of broken rules in the collection that have a severity of Error.
Holds broken rules for an Node in the BrokenRulesTree.
Holds a list of broken rules tree list
Manages the list of rules for a business type.
List< IBusinessRuleBase > Rules
Gets the list of rule objects for the business type.
Tracks the business rules for a business object.
BusinessRules()
Creates an instance of the object.
static bool HasPermission(AuthorizationActions action, Type objectType)
Checks per-type authorization rules.
string?? RuleSet
Gets or sets the rule set to use for this business object instance.
async Task< List< string > > CheckRulesAsync()
Invokes all rules for the business type.
int ProcessThroughPriority
Gets or sets the priority through which all rules will be processed.
List< string > CheckRules()
Invokes all rules for the business type.
override void OnSetChildren(SerializationInfo info, MobileFormatter formatter)
Override this method to retrieve your child object references from the MobileFormatter serialzation s...
static BrokenRulesTree GetAllBrokenRules(object root, bool errorsOnly)
Gets all nodes in tree that have broken rules.
async Task< List< string > > CheckRulesAsync(int timeout)
Invokes all rules for the business type.
bool SuppressRuleChecking
Gets or sets a value indicating whether calling CheckRules should result in rule methods being invoke...
static bool HasPermission(AuthorizationActions action, Type objectType, object[] criteria)
Checks per-type authorization rules.
bool IsValid
Gets a value indicating whether there are any currently broken rules, which would mean the object is ...
void AddRule(IAuthorizationRule rule)
Associates an authorization rule with the business object.
bool CachePermissionResult(AuthorizationActions action, Csla.Core.IMemberInfo element)
Gets a value indicating whether the permission result can be cached.
static bool HasPermission(AuthorizationActions action, object obj, string ruleSet)
Checks per-instance authorization rules.
void AddRule(IBusinessRuleBase rule, string ruleSet)
Associates a business rule with the business object.
bool RunningRules
Gets a value indicating whether a CheckRules operation is in progress.
bool HasPermission(AuthorizationActions action, Csla.Core.IMemberInfo element)
Checks per-property authorization rules.
bool CascadeOnDirtyProperties
Gets or sets a value indicating whether rule engine should cacade n-leves when property value is chan...
List< string > CheckObjectRules()
Invokes all rules attached at the class level of the business type.
static void AddRule(Type objectType, IAuthorizationRule rule, string ruleSet)
Associates a per-type authorization rule with the business type.
static BrokenRulesTree GetAllBrokenRules(object root)
Gets all nodes in tree that have IsValid = false (and all parents)
void AddDataAnnotations()
Adds validation rules corresponding to property data annotation attributes.
override void OnGetState(SerializationInfo info, StateMode mode)
Override this method to insert your field values into the MobileFormatter serialzation stream.
List< string > CheckRules(Csla.Core.IPropertyInfo property)
Invokes all rules for a specific property of the business type.
bool GetPropertyBusy(Csla.Core.IPropertyInfo property)
Gets a value indicating whether a specific property has any async rules running.
void AddRule(IBusinessRuleBase rule)
Associates a business rule with the business object.
bool RunningAsyncRules
Gets a value indicating whether any async rules are currently executing.
override void OnSetState(SerializationInfo info, StateMode mode)
Override this method to retrieve your field values from the MobileFormatter serialzation stream.
static void AddRule(Type objectType, IAuthorizationRule rule)
Associates a per-type authorization rule with the business type in the default rule set.
static bool HasPermission(AuthorizationActions action, Type objectType, string ruleSet)
Checks per-type authorization rules.
BrokenRulesCollection GetBrokenRules()
Gets the broken rules list.
string[] GetRuleDescriptions()
Gets a list of rule:// URI values for the rules defined in the object.
static bool HasPermission(AuthorizationActions action, object obj)
Checks per-instance authorization rules.
override void OnGetChildren(SerializationInfo info, MobileFormatter formatter)
Override this method to insert your child object references into the MobileFormatter serialzation str...
Object containing the serialization data for a specific object.
int ReferenceId
Reference number for this object.
Dictionary< string, ChildData > Children
Dictionary containing child reference data.
void AddChild(string name, int referenceId)
Adds a child to the list of child references.
void AddValue(string name, object value)
Adds a value to the serialization stream.
Async/await implementation of a ManualResetEvent
void Reset()
Reset the event, preparing it for reuse
Task WaitAsync()
Get awaitable task for the event
void Set()
Set the event, unblocking any code awaiting the event
Defines the common methods required by all editable CSLA collection objects.
Maintains metadata about a method or property.
string Name
Gets the member name value.
Maintains metadata about a property.
Defines the common properties required objects that track their own status.
Interface defining an authorization rule implementation.
Csla.Core.IMemberInfo Element
Gets the element (property/method) to which this rule is associated.
AuthorizationActions Action
Gets the authorization action this rule will enforce.
Interface defining a business/validation rule implementation.
Public interfacefor IBusinessRules
Defines the interaction between the rules engine and a business object that hosts the rules.
void RuleStart(Csla.Core.IPropertyInfo property)
Indicates that a rule has started processing.
void AllRulesComplete()
Indicates that all rules have finished processing.
Interface defining callback methods used by the SerializationFormatterFactory.GetFormatter().
void Deserialized()
Method called on an object after deserialization is complete.
StateMode
Indicates the reason the MobileFormatter functionality has been invoked.
RunModes
Flags enum to define when rule is allowed or denied to run
AuthorizationActions
Authorization actions.
RuleContextModes
RuleContext mode flags
RelationshipTypes
List of valid relationship types between a parent object and another object through a managed propert...
@ Serializable
Prevents updating or inserting until the transaction is complete.