CSLA.NET 6.0.0
CSLA .NET is a software development framework that helps you build a reusable, maintainable object-oriented business layer for your app.
BusinessRules.cs
Go to the documentation of this file.
1//-----------------------------------------------------------------------
2// <copyright file="BusinessRules.cs" company="Marimer LLC">
3// Copyright (c) Marimer LLC. All rights reserved.
4// Website: https://cslanet.com
5// </copyright>
6// <summary>Tracks the business rules for a business object.</summary>
7//-----------------------------------------------------------------------
8using System;
9using System.Collections;
10using System.Linq;
11using System.Collections.Generic;
13using Csla.Core;
14using System.Threading.Tasks;
15using Csla.Threading;
16using System.Security.Principal;
17
18namespace Csla.Rules
19{
24 public class BusinessRules :
26 {
31 { }
32
38 public BusinessRules(ApplicationContext applicationContext, IHostRules target)
39 {
40 ApplicationContext = applicationContext;
41 SetTarget(target);
42 }
43
44 [NonSerialized]
45 private object SyncRoot = new();
46
48 private ApplicationContext ApplicationContext { get; set; }
49
50 // list of broken rules for this business object.
51 private BrokenRulesCollection _brokenRules;
52 private BrokenRulesCollection BrokenRules
53 {
54 get
55 {
56 if (_brokenRules == null)
57 _brokenRules = new BrokenRulesCollection(true);
58 return _brokenRules;
59 }
60 }
61
62 private bool _suppressRuleChecking;
70 {
71 get { return _suppressRuleChecking; }
72 set { _suppressRuleChecking = value; }
73 }
74
75 private int _processThroughPriority;
81 {
82 get { return _processThroughPriority; }
83 set { _processThroughPriority = value; }
84 }
85
86 private string _ruleSet = null;
91 public string RuleSet
92 {
93 get { return string.IsNullOrEmpty(_ruleSet) ? ApplicationContext.DefaultRuleSet : _ruleSet; }
94 set
95 {
96 _typeRules = null;
97 _typeAuthRules = null;
98 _ruleSet = value == ApplicationContext.DefaultRuleSet ? null : value;
99 if (BrokenRules.Count > 0)
100 {
101 BrokenRules.ClearRules();
102 }
103 }
104 }
105
113 {
114 get { return _cascadeOnDirtyProperties; }
115 set { _cascadeOnDirtyProperties = value; }
116 }
117
118
119 [NonSerialized]
120 private BusinessRuleManager _typeRules;
121 internal BusinessRuleManager TypeRules
122 {
123 get
124 {
125 if (_typeRules == null && _target != null)
126 _typeRules = BusinessRuleManager.GetRulesForType(_target.GetType(), _ruleSet);
127 return _typeRules;
128 }
129 }
130
131 [NonSerialized]
132 private AuthorizationRuleManager _typeAuthRules;
133 internal AuthorizationRuleManager TypeAuthRules
134 {
135 get
136 {
137 if (_typeAuthRules == null && _target != null)
138 _typeAuthRules = AuthorizationRuleManager.GetRulesForType(ApplicationContext, _target.GetType(), _ruleSet);
139 return _typeAuthRules;
140 }
141 }
142
148 public string[] GetRuleDescriptions()
149 {
150 var result = new List<string>();
151 foreach (var item in TypeRules.Rules)
152 result.Add(item.RuleName);
153 return result.ToArray();
154 }
155
156 // reference to current business object
157 [NonSerialized]
158 private IHostRules _target;
159
160 internal void SetTarget(IHostRules target)
161 {
162 _target = target;
163 }
164
165 internal object Target
166 {
167 get { return _target; }
168 }
169
174 public void AddRule(IBusinessRuleBase rule)
175 {
176 TypeRules.Rules.Add(rule);
177 }
178
184 public void AddRule(IBusinessRuleBase rule, string ruleSet)
185 {
186 var typeRules = BusinessRuleManager.GetRulesForType(_target.GetType(), ruleSet);
187 typeRules.Rules.Add(rule);
188 }
189
194 public void AddRule(IAuthorizationRule rule)
195 {
196 EnsureUniqueRule(TypeAuthRules, rule);
197 TypeAuthRules.Rules.Add(rule);
198 }
199
206 public static void AddRule(Type objectType, IAuthorizationRule rule)
207 {
208 AddRule(objectType, rule, ApplicationContext.DefaultRuleSet);
209 }
210
218 public static void AddRule(Type objectType, IAuthorizationRule rule, string ruleSet)
219 {
220 AddRule(null, objectType, rule, ruleSet);
221 }
222
231 public static void AddRule(ApplicationContext applicationContext, Type objectType, IAuthorizationRule rule, string ruleSet)
232 {
233 var typeRules = AuthorizationRuleManager.GetRulesForType(applicationContext, objectType, ruleSet);
234 EnsureUniqueRule(typeRules, rule);
235 typeRules.Rules.Add(rule);
236 }
237
238 private static void EnsureUniqueRule(AuthorizationRuleManager mgr, IAuthorizationRule rule)
239 {
240 IAuthorizationRule oldRule = null;
241 if (rule.Element != null)
242 oldRule = mgr.Rules.FirstOrDefault(c => c.Element != null && c.Element.Name == rule.Element.Name && c.Action == rule.Action);
243 else
244 oldRule = mgr.Rules.FirstOrDefault(c => c.Element == null && c.Action == rule.Action);
245 if (oldRule != null)
246 throw new ArgumentException(nameof(rule));
247 }
248
254 public bool IsValid
255 {
256 get { return BrokenRules.ErrorCount == 0; }
257 }
258
263 {
264 return BrokenRules;
265 }
266
267 [NonSerialized]
268 private bool _runningRules;
273 public bool RunningRules
274 {
275 get { return _runningRules; }
276 private set { _runningRules = value; }
277 }
278
279 [NonSerialized]
280 private bool _isBusy;
281
282 [NonSerialized]
283 private AsyncManualResetEvent _busyChanged;
284 private AsyncManualResetEvent BusyChanged
285 {
286 get
287 {
288 if (_busyChanged == null)
289 _busyChanged = new AsyncManualResetEvent();
290 return _busyChanged;
291 }
292 }
293
299 {
300 get { return _isBusy; }
301 set
302 {
303 _isBusy = value;
304 if (_isBusy)
305 BusyChanged.Reset();
306 else
307 BusyChanged.Set();
308 }
309 }
310
317 {
318 return BusyProperties.Contains(property);
319 }
320
327 public static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Type objectType)
328 {
329 if (applicationContext == null)
330 throw new ArgumentNullException(nameof(applicationContext));
331 // no object specified so must use RuleSet from ApplicationContext
332 return HasPermission(action, null, applicationContext, objectType, null, applicationContext.RuleSet);
333 }
334
342 public static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Type objectType, object[] criteria)
343 {
344 if (applicationContext == null)
345 throw new ArgumentNullException(nameof(applicationContext));
346 // no object specified so must use RuleSet from ApplicationContext
347 return HasPermission(action, null, applicationContext, objectType, criteria, applicationContext.RuleSet);
348 }
349
360 public static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Type objectType, string ruleSet)
361 {
362 if (applicationContext == null)
363 throw new ArgumentNullException(nameof(applicationContext));
364 return HasPermission(action, null, applicationContext, objectType, null, ruleSet);
365 }
366
373 public static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, object obj)
374 {
375 if (applicationContext == null)
376 throw new ArgumentNullException(nameof(applicationContext));
377 return HasPermission(action, obj, applicationContext, obj.GetType(), null, applicationContext.RuleSet);
378 }
379
380 private static bool HasPermission(AuthorizationActions action, object obj, ApplicationContext applicationContext, Type objType, object[] criteria, string ruleSet)
381 {
382
383 if (action == AuthorizationActions.ReadProperty ||
384 action == AuthorizationActions.WriteProperty ||
385 action == AuthorizationActions.ExecuteMethod)
386 throw new ArgumentOutOfRangeException($"{nameof(action)}, {action}");
387
388 bool result = true;
389 var rule =
390 AuthorizationRuleManager.GetRulesForType(applicationContext, objType, ruleSet).Rules.FirstOrDefault(c => c.Element == null && c.Action == action);
391 if (rule != null)
392 {
393 var context = new AuthorizationContext(applicationContext, rule, obj, objType) { Criteria = criteria };
394 rule.Execute(context);
395 result = context.HasPermission;
396 }
397 return result;
398 }
399
406 public bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Csla.Core.IMemberInfo element)
407 {
408 if (_suppressRuleChecking)
409 return true;
410
411 if (action == AuthorizationActions.CreateObject ||
412 action == AuthorizationActions.DeleteObject ||
413 action == AuthorizationActions.GetObject ||
414 action == AuthorizationActions.EditObject)
415 throw new ArgumentOutOfRangeException($"{nameof(action)}, {action}");
416
417 bool result = true;
418 var rule =
419 TypeAuthRules.Rules.FirstOrDefault(c => c.Element != null && c.Element.Name == element.Name && c.Action == action);
420 if (rule != null)
421 {
422 var context = new AuthorizationContext(applicationContext, rule, this.Target, this.Target.GetType());
423 rule.Execute(context);
424 result = context.HasPermission;
425 }
426 return result;
427 }
428
436 {
437 // cannot cache result when suppressRuleChecking as HasPermission is then short circuited to return true.
438 if (_suppressRuleChecking)
439 return false;
440
441 bool result = true;
442 var rule =
443 TypeAuthRules.Rules.FirstOrDefault(c => c.Element != null && c.Element.Name == element.Name && c.Action == action);
444 if (rule != null)
445 result = rule.CacheResult;
446 return result;
447 }
448
458 public async Task<List<string>> CheckRulesAsync(int timeout)
459 {
460 var result = CheckRules();
462 {
463 var tasks = new Task[] { BusyChanged.WaitAsync(), Task.Delay(timeout) };
464 var final = await Task.WhenAny(tasks);
465 if (final == tasks[1])
466 throw new TimeoutException(nameof(CheckRulesAsync));
467 }
468 return result;
469 }
470
479 public async Task<List<string>> CheckRulesAsync()
480 {
481 return await CheckRulesAsync(int.MaxValue);
482 }
483
492 public List<string> CheckRules()
493 {
494 if (_suppressRuleChecking)
495 return new List<string>();
496
497 RunningRules = true;
498 var affectedProperties = CheckObjectRules(RuleContextModes.CheckRules, false);
499 var properties = TypeRules.Rules.Where(p => p.PrimaryProperty != null)
500 .Select(p => p.PrimaryProperty)
501 .Distinct();
502 foreach (var property in properties)
503 affectedProperties.AddRange(CheckRules(property, RuleContextModes.CheckRules));
504 RunningRules = false;
506 _target.AllRulesComplete();
507 return affectedProperties.Distinct().ToList();
508 }
509
519 public List<string> CheckObjectRules()
520 {
521 return CheckObjectRules(RuleContextModes.CheckObjectRules, true);
522 }
523
524
534 private List<string> CheckObjectRules(RuleContextModes executionContext, bool cascade)
535 {
536 if (_suppressRuleChecking)
537 return new List<string>();
538
539 var oldRR = RunningRules;
540 RunningRules = true;
541 var rules = from r in TypeRules.Rules
542 where r.PrimaryProperty == null
543 && CanRunRule(ApplicationContext, r, executionContext)
544 orderby r.Priority
545 select r;
546 BrokenRules.ClearRules(null);
547 // Changed to cascade propertyrule to make async ObjectLevel rules rerun PropertLevel rules.
548 var firstResult = RunRules(rules, false, executionContext);
549
550 // rerun property level rules for affected properties
551 if (cascade)
552 {
553 var propertiesToRun = new List<Csla.Core.IPropertyInfo>();
554 foreach (var item in rules)
555 if (!item.IsAsync)
556 {
557 foreach (var p in item.AffectedProperties)
558 propertiesToRun.Add(p);
559 }
560 // run rules for affected properties
561 foreach (var item in propertiesToRun.Distinct())
562 {
563 var doCascade = false;
565 doCascade = firstResult.DirtyProperties.Any(p => p == item.Name);
566 firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade,
567 executionContext | RuleContextModes.AsAffectedPoperty));
568 }
569 }
570
571 RunningRules = oldRR;
573 _target.AllRulesComplete();
574 return firstResult.AffectedProperties.Distinct().ToList();
575 }
576
587 public List<string> CheckRules(Csla.Core.IPropertyInfo property)
588 {
589 return CheckRules(property, RuleContextModes.PropertyChanged);
590 }
591
592 private List<string> CheckRules(Csla.Core.IPropertyInfo property, RuleContextModes executionContext)
593 {
594 if (property == null)
595 throw new ArgumentNullException(nameof(property));
596
597 if (_suppressRuleChecking)
598 return new List<string>();
599
600 var oldRR = RunningRules;
601 RunningRules = true;
602
603 var affectedProperties = new List<string>();
604 affectedProperties.AddRange(CheckRulesForProperty(property, true, executionContext));
605
606 RunningRules = oldRR;
608 _target.AllRulesComplete();
609 return affectedProperties.Distinct().ToList();
610 }
611
621 internal static bool CanRunRule(ApplicationContext applicationContext, IBusinessRuleBase rule, RuleContextModes contextMode)
622 {
623 // default then just return true
624 if (rule.RunMode == RunModes.Default) return true;
625
626 bool canRun = true;
627
628 if ((contextMode & RuleContextModes.AsAffectedPoperty) > 0)
629 canRun &= (rule.RunMode & RunModes.DenyAsAffectedProperty) == 0;
630
631 if ((rule.RunMode & RunModes.DenyOnServerSidePortal) > 0)
632 canRun &= applicationContext.LogicalExecutionLocation != ApplicationContext.LogicalExecutionLocations.Server;
633
634 if ((contextMode & RuleContextModes.CheckRules) > 0)
635 canRun &= (rule.RunMode & RunModes.DenyCheckRules) == 0;
636
637 return canRun;
638 }
639
647 private List<string> CheckRulesForProperty(Csla.Core.IPropertyInfo property, bool cascade, RuleContextModes executionContext)
648 {
649 var rules = from r in TypeRules.Rules
650 where ReferenceEquals(r.PrimaryProperty, property)
651 && CanRunRule(ApplicationContext, r, executionContext)
652 orderby r.Priority
653 select r;
654
655 BrokenRules.ClearRules(property);
656 var firstResult = RunRules(rules, cascade, executionContext);
658 cascade = cascade || firstResult.DirtyProperties.Any();
659 if (cascade)
660 {
661 // get properties affected by all rules
662 var propertiesToRun = new List<Csla.Core.IPropertyInfo>();
663 foreach (var item in rules)
664 if (!item.IsAsync)
665 {
666 foreach (var p in item.AffectedProperties)
667 if (!ReferenceEquals(property, p))
668 propertiesToRun.Add(p);
669 }
670
671 // add PrimaryProperty where property is in InputProperties
672 var input = from r in TypeRules.Rules
673 where !ReferenceEquals(r.PrimaryProperty, property)
674 && r.PrimaryProperty != null
675 && r.InputProperties != null
676 && r.InputProperties.Contains(property)
677 select r.PrimaryProperty;
678
679 foreach (var p in input)
680 {
681 if (!ReferenceEquals(property, p))
682 propertiesToRun.Add(p);
683 }
684 // run rules for affected properties
685 foreach (var item in propertiesToRun.Distinct())
686 {
687 var doCascade = false;
689 doCascade = firstResult.DirtyProperties.Any(p => p == item.Name);
690 firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade,
691 executionContext | RuleContextModes.AsAffectedPoperty));
692 }
693 }
694
695 // always make sure to add PrimaryProperty
696 firstResult.AffectedProperties.Add(property.Name);
697 return firstResult.AffectedProperties.Distinct().ToList();
698 }
699
700 [NonSerialized]
701 private List<Csla.Core.IPropertyInfo> _busyProperties;
702
703 private bool _cascadeOnDirtyProperties;
704
705 private List<Csla.Core.IPropertyInfo> BusyProperties
706 {
707 get
708 {
709 if (_busyProperties == null)
710 _busyProperties = new List<Csla.Core.IPropertyInfo>();
711 return _busyProperties;
712 }
713 }
714
722 private RunRulesResult RunRules(IEnumerable<IBusinessRuleBase> rules, bool cascade, RuleContextModes executionContext)
723 {
724 var affectedProperties = new List<string>();
725 var dirtyProperties = new List<string>();
726 bool anyRuleBroken = false;
727 foreach (var rule in rules)
728 {
729 // implicit short-circuiting
730 if (anyRuleBroken && rule.Priority > ProcessThroughPriority)
731 break;
732 bool complete = false;
733 // set up context
734 var context = new RuleContext(ApplicationContext, (r) =>
735 {
736 if (r.Rule.IsAsync)
737 {
738 lock (SyncRoot)
739 {
740 // update output values
741 if (r.OutputPropertyValues != null)
742 foreach (var item in r.OutputPropertyValues)
743 {
744 // value is changed add to dirtyValues
745 if (((IManageProperties)_target).LoadPropertyMarkDirty(item.Key, item.Value))
746 r.AddDirtyProperty(item.Key);
747 }
748 // update broken rules list
749 BrokenRules.SetBrokenRules(r.Results, r.OriginPropertyName);
750
751 // run rules on affected properties for this async rule
752 var affected = new List<string>();
753 if (cascade)
754 foreach (var item in r.Rule.AffectedProperties.Distinct())
755 if (!ReferenceEquals(r.Rule.PrimaryProperty, item))
756 {
757 var doCascade = false;
758 if (CascadeOnDirtyProperties && (r.DirtyProperties != null))
759 doCascade = r.DirtyProperties.Any(p => p.Name == item.Name);
760 affected.AddRange(CheckRulesForProperty(item, doCascade, r.ExecuteContext | RuleContextModes.AsAffectedPoperty));
761 }
762
763 // mark each property as not busy
764 foreach (var item in r.Rule.AffectedProperties)
765 {
766 BusyProperties.Remove(item);
767 RunningAsyncRules = BusyProperties.Count > 0;
768 if (!BusyProperties.Contains(item))
769 _target.RuleComplete(item);
770 }
771
772 foreach (var property in affected.Distinct())
773 {
774 // property is not in AffectedProperties (already signalled to UI)
775 if (!r.Rule.AffectedProperties.Any(p => p.Name == property))
776 _target.RuleComplete(property);
777 }
778
779 if (!RunningRules && !RunningAsyncRules)
780 _target.AllRulesComplete();
781 }
782 }
783 else // Rule is Sync
784 {
785 // update output values
786 if (r.OutputPropertyValues != null)
787 foreach (var item in r.OutputPropertyValues)
788 {
789 // value is changed add to dirtyValues
790 if (((IManageProperties)_target).LoadPropertyMarkDirty(item.Key, item.Value))
791 r.AddDirtyProperty(item.Key);
792 }
793
794 // update broken rules list
795 if (r.Results != null)
796 {
797 BrokenRules.SetBrokenRules(r.Results, r.OriginPropertyName);
798
799 // is any rules here broken with severity Error
800 if (r.Results.Any(p => !p.Success && p.Severity == RuleSeverity.Error))
801 anyRuleBroken = true;
802 }
803
804 complete = true;
805 }
806 })
807 {
808 Rule = rule
809 };
810 if (rule.PrimaryProperty != null)
811 context.OriginPropertyName = rule.PrimaryProperty.Name;
812 context.ExecuteContext = executionContext;
813 if (!rule.IsAsync || rule.ProvideTargetWhenAsync)
814 context.Target = _target;
815
816 // get input properties
817 if (rule.InputProperties != null)
818 {
819 var target = (IManageProperties) _target;
820 context.InputPropertyValues = new Dictionary<IPropertyInfo, object>();
821 foreach (var item in rule.InputProperties)
822 {
823 // do not add lazy loaded fields that have no field data.
824 if ((item.RelationshipType & RelationshipTypes.LazyLoad) == RelationshipTypes.LazyLoad)
825 {
826 if (target.FieldExists(item))
827 context.InputPropertyValues.Add(item, target.ReadProperty(item));
828 }
829 else
830 context.InputPropertyValues.Add(item, target.ReadProperty(item));
831 }
832 }
833
834 // mark properties busy
835 if (rule.IsAsync)
836 {
837 lock (SyncRoot)
838 {
839 // mark each property as busy
840 foreach (var item in rule.AffectedProperties)
841 {
842 var alreadyBusy = BusyProperties.Contains(item);
843 BusyProperties.Add(item);
844 RunningAsyncRules = true;
845 if (!alreadyBusy)
846 _target.RuleStart(item);
847 }
848 }
849 }
850
851 // execute (or start executing) rule
852 try
853 {
854 if (rule is IBusinessRule syncRule)
855 syncRule.Execute(context);
856 else if (rule is IBusinessRuleAsync asyncRule)
857 RunAsyncRule(asyncRule, context);
858 else
859 throw new ArgumentOutOfRangeException(rule.GetType().FullName);
860 }
861 catch (Exception ex)
862 {
863 context.AddErrorResult(string.Format("{0}:{1}", rule.RuleName, ex.Message));
864 if (rule.IsAsync)
865 context.Complete();
866 }
867
868 if (!rule.IsAsync)
869 {
870 // process results
871 if (!complete)
872 context.Complete();
873 // copy affected property names
874 affectedProperties.AddRange(rule.AffectedProperties.Select(c => c.Name));
875 // copy output property names
876 if (context.OutputPropertyValues != null)
877 affectedProperties.AddRange(context.OutputPropertyValues.Select(c =>c.Key.Name));
878 // copy dirty properties
879 if (context.DirtyProperties != null)
880 dirtyProperties.AddRange(context.DirtyProperties.Select(c => c.Name));
881
882 if (context.Results != null)
883 {
884 // explicit short-circuiting
885 if (context.Results.Any(r => r.StopProcessing))
886 break;
887 }
888 }
889 }
890 // return any synchronous results
891 return new RunRulesResult(affectedProperties, dirtyProperties);
892 }
893
894 private static async void RunAsyncRule(IBusinessRuleAsync asyncRule, IRuleContext context)
895 {
896 try
897 {
898 await asyncRule.ExecuteAsync(context);
899 }
900 finally
901 {
902 context.Complete();
903 }
904 }
905
906 #region DataAnnotations
907
912 [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
913 public void AddDataAnnotations()
914 {
915 Type metadataType;
916#if !NETSTANDARD2_0 && !NET5_0 && !NET6_0
917 // add data annotations from metadata class if specified
918 var classAttList = _target.GetType().GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.MetadataTypeAttribute), true);
919 if (classAttList.Length > 0)
920 {
921 metadataType = ((System.ComponentModel.DataAnnotations.MetadataTypeAttribute)classAttList[0]).MetadataClassType;
922 AddDataAnnotationsFromType(metadataType);
923 }
924#endif
925
926 // attributes on class
927 metadataType = _target.GetType();
928 AddDataAnnotationsFromType(metadataType);
929 }
930
935 private void AddDataAnnotationsFromType(Type metadataType)
936 {
937 var attList = metadataType.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.ValidationAttribute), true);
938 foreach (var att in attList)
939 AddRule(new CommonRules.DataAnnotation(null, (System.ComponentModel.DataAnnotations.ValidationAttribute)att));
940
941 // attributes on properties
942 var propList = metadataType.GetProperties();
943 foreach (var prop in propList)
944 {
945 attList = prop.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.ValidationAttribute), true);
946 foreach (var att in attList)
947 {
948 var target = (IManageProperties)_target;
949 var pi = target.GetManagedProperties().First(c => c.Name == prop.Name);
950 AddRule(new CommonRules.DataAnnotation(pi, (System.ComponentModel.DataAnnotations.ValidationAttribute)att));
951 }
952 }
953 }
954
955 #endregion
956
957 #region MobileObject overrides
958
969 protected override void OnGetState(SerializationInfo info, StateMode mode)
970 {
971 info.AddValue("_processThroughPriority", _processThroughPriority);
972 info.AddValue("_ruleSet", _ruleSet);
973 info.AddValue("_cascadeWhenChanged", _cascadeOnDirtyProperties);
974 base.OnGetState(info, mode);
975 }
976
987 protected override void OnSetState(SerializationInfo info, StateMode mode)
988 {
989 _processThroughPriority = info.GetValue<int>("_processThroughPriority");
990 _ruleSet = info.GetValue<string>("_ruleSet");
991 _cascadeOnDirtyProperties = info.GetValue<bool>("_cascadeWhenChanged");
992 base.OnSetState(info, mode);
993 }
994
1006 protected override void OnGetChildren(SerializationInfo info, MobileFormatter formatter)
1007 {
1008 if (_brokenRules != null && _brokenRules.Count > 0)
1009 {
1010 SerializationInfo brInfo = formatter.SerializeObject(_brokenRules);
1011 info.AddChild("_brokenRules", brInfo.ReferenceId);
1012 }
1013
1014 base.OnGetChildren(info, formatter);
1015 }
1016
1028 protected override void OnSetChildren(SerializationInfo info, MobileFormatter formatter)
1029 {
1030 if (info.Children.ContainsKey("_brokenRules"))
1031 {
1032 int referenceId = info.Children["_brokenRules"].ReferenceId;
1033 _brokenRules = (BrokenRulesCollection)formatter.GetObject(referenceId);
1034 }
1035
1036 base.OnSetChildren(info, formatter);
1037 }
1038 #endregion
1039
1040 #region Serialization Notification
1041
1043 {
1044 OnDeserializedHandler(new System.Runtime.Serialization.StreamingContext());
1045 }
1046
1047 [System.Runtime.Serialization.OnDeserialized]
1048 private void OnDeserializedHandler(System.Runtime.Serialization.StreamingContext context)
1049 {
1050 SyncRoot = new object();
1051 }
1052
1053 #endregion
1054
1055 #region Get All Broken Rules (tree)
1056
1062 public static BrokenRulesTree GetAllBrokenRules(object root)
1063 {
1064 return GetAllBrokenRules(root, true);
1065 }
1072 public static BrokenRulesTree GetAllBrokenRules(object root, bool errorsOnly)
1073 {
1074 var list = new BrokenRulesTree();
1075 long counter = 1;
1076 long childBrokenRuleCount = 0;
1077 AddNodeToBrukenRules(ref list, ref counter, null, root, errorsOnly, ref childBrokenRuleCount);
1078
1079 return list;
1080 }
1081
1082 private static void AddNodeToBrukenRules(ref BrokenRulesTree list, ref long counter, object parentKey, object obj, bool errorsOnly, ref long childBrokenRuleCount)
1083 {
1084 // is this a single editable object
1085 if (obj is Csla.Core.BusinessBase bbase)
1086 {
1087 var nodeKey = counter++;
1088 var bo = bbase;
1089 long myChildBrokenRuleCount = bo.BrokenRulesCollection.Count;
1090 var node = new BrokenRulesNode() { Parent = parentKey, Node = nodeKey, BrokenRules = bo.BrokenRulesCollection, Object = obj };
1091 list.Add(node);
1092
1093 // get managed child properties
1094 foreach (var child in ((IManageProperties)bo).GetChildren())
1095 {
1096 AddNodeToBrukenRules(ref list, ref counter, nodeKey, child, errorsOnly, ref myChildBrokenRuleCount);
1097 }
1098
1099 // remove node if it has no child with broken rules.
1100 if (!errorsOnly && myChildBrokenRuleCount == 0)
1101 {
1102 list.Remove(node);
1103 }
1104 if (errorsOnly && bo.IsValid)
1105 {
1106 list.Remove(node);
1107 }
1108 childBrokenRuleCount += myChildBrokenRuleCount;
1109 }
1110
1111 // or a list of EditableObject (both BindingList and ObservableCollection)
1112 else if (obj is IEditableCollection)
1113 {
1114 var nodeKey = counter++;
1115 var isValid = ((ITrackStatus)obj).IsValid;
1116 var node = new BrokenRulesNode() { Parent = parentKey, Node = nodeKey, Object = obj, BrokenRules = new BrokenRulesCollection(true) };
1117 long myChildBrokenRuleCount = 0;
1118
1119 list.Add(node);
1120
1121 foreach (var child in (IEnumerable)obj)
1122 {
1123 AddNodeToBrukenRules(ref list, ref counter, nodeKey, child, errorsOnly, ref myChildBrokenRuleCount);
1124 }
1125
1126 // remove node if it has no child with broken rules.
1127 if (!errorsOnly && myChildBrokenRuleCount == 0)
1128 {
1129 list.Remove(node);
1130 }
1131 if (errorsOnly && isValid)
1132 {
1133 list.Remove(node);
1134 }
1135 childBrokenRuleCount += myChildBrokenRuleCount;
1136 }
1137 return;
1138 }
1139
1140 #endregion
1141
1142
1143 internal class RunRulesResult
1144 {
1145 public RunRulesResult(List<string> affectedProperties, List<string> dirtyProperties)
1146 {
1147 AffectedProperties = affectedProperties;
1148 DirtyProperties = dirtyProperties;
1149 }
1150
1151 public List<string> AffectedProperties { get; set; }
1152 public List<string> DirtyProperties { get; set; }
1153 }
1154
1155 object IBusinessRules.Target
1156 {
1157 get { return Target; }
1158 }
1159 }
1160}
Provides consistent context information between the client and server DataPortal objects.
LogicalExecutionLocations LogicalExecutionLocation
Return Logical Execution Location - Client or Server This is applicable to Local mode as well
LogicalExecutionLocations
Enum representing the logical execution location The setting is set to server when server is execting...
string? RuleSet
Gets or sets the RuleSet name to use for static HasPermission calls.
const string DefaultRuleSet
The default RuleSet name
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.
Definition: MobileObject.cs:20
Context information provided to an authorization rule when it is invoked.
bool HasPermission
Gets or sets a value indicating whether the current user has permission to perform the requested acti...
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 type.
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.
static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Type objectType, object[] criteria)
Checks per-type authorization rules.
async Task< List< string > > CheckRulesAsync(int timeout)
Invokes all rules for the business type.
static void AddRule(ApplicationContext applicationContext, Type objectType, IAuthorizationRule rule, string ruleSet)
Associates a per-type authorization rule with the business type.
bool SuppressRuleChecking
Gets or sets a value indicating whether calling CheckRules should result in rule methods being invoke...
bool IsValid
Gets a value indicating whether there are any currently broken rules, which would mean the object is ...
static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, object obj)
Checks per-instance authorization rules.
void AddRule(IAuthorizationRule rule)
Associates an authorization rule with the business object.
BusinessRules(ApplicationContext applicationContext, IHostRules target)
Creates an instance of the type.
void AddRule(IBusinessRuleBase rule, string ruleSet)
Associates a business rule with the business object.
bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Csla.Core.IMemberInfo element)
Checks per-property authorization rules.
bool RunningRules
Gets a value indicating whether a CheckRules operation is in progress.
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.
static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Type objectType)
Checks per-type authorization rules.
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.
bool CachePermissionResult(AuthorizationActions action, IMemberInfo element)
Gets a value indicating whether the permission result can be cached.
static void AddRule(Type objectType, IAuthorizationRule rule)
Associates a per-type authorization rule with the business type in the default rule set.
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(ApplicationContext applicationContext, AuthorizationActions action, Type objectType, string ruleSet)
Checks per-type authorization rules.
override void OnGetChildren(SerializationInfo info, MobileFormatter formatter)
Override this method to insert your child object references into the MobileFormatter serialzation str...
Serializes and deserializes objects at the field level.
IMobileObject GetObject(int referenceId)
Gets a deserialized object based on the object's reference id within the serialization stream.
SerializationInfo SerializeObject(object obj)
Serializes an object into a SerializationInfo object.
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.
Definition: IMemberInfo.cs:19
string Name
Gets the member name value.
Definition: IMemberInfo.cs:23
Maintains metadata about a property.
Defines the common properties required objects that track their own status.
Definition: ITrackStatus.cs:17
Implement if a class requires access to the CSLA ApplicationContext type.
ApplicationContext ApplicationContext
Gets or sets the current ApplicationContext object.
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.
Definition: IHostRules.cs:20
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.
Definition: StateMode.cs:20
RunModes
Flags enum to define when rule is allowed or denied to run
AuthorizationActions
Authorization actions.
RuleContextModes
RuleContext mode flags
Definition: RuleContext.cs:23
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.