Inheritance: IAsyncValidationRule
 private void UnregisterValidationRule(ValidationRule rule)
 {
     lock (syncRoot)
     {
         ValidationRules.Remove(rule);
     }
 }
 private void RegisterValidationRule(ValidationRule rule)
 {
     lock (syncRoot)
     {
         ValidationRules.Add(rule);
     }
 }
        private void SaveRuleValidationResultAndNotifyIfNeeded(ValidationRule rule, RuleResult ruleResult, SynchronizationContext syncContext)
        {
            lock (syncRoot)
            {
                IEnumerable<object> ruleTargets = rule.Target.UnwrapTargets();

                foreach (object ruleTarget in ruleTargets)
                {
                    IDictionary<ValidationRule, RuleResult> targetRuleMap = GetRuleMapForTarget(ruleTarget);

                    RuleResult currentRuleResult = GetCurrentValidationResultForRule(targetRuleMap, rule);

                    if (!Equals(currentRuleResult, ruleResult))
                    {
                        lock (ruleValidationResultMap)
                        {
                            targetRuleMap[rule] = ruleResult;
                        }

                        // Notify that validation result for the target has changed
                        NotifyResultChanged(ruleTarget, GetResult(ruleTarget), syncContext);
                    }
                }
            }
        }
        private IAsyncValidationRule AddRuleCore(IValidationTarget target, Func<RuleResult> validateDelegate,
            Func<Task<RuleResult>> asyncValidateAction)
        {
            var rule = new ValidationRule(target, validateDelegate, asyncValidateAction);

            RegisterValidationRule(rule);

            return rule;
        }
        private RuleResult ExecuteRuleCore(ValidationRule rule)
        {
            RuleResult result = RuleResult.Valid();

            if (!rule.SupportsSyncValidation)
            {
                var completedEvent = new ManualResetEvent(false);

                rule.EvaluateAsync().ContinueWith(t =>
                {
                    result = t.Result;
                    completedEvent.Set();
                });

                var isCompleted = completedEvent.WaitOne(AsyncRuleExecutionTimeout);

                if (!isCompleted)
                {
                    throw new TimeoutException("Rule validation did not complete in specified timeout.");
                }
            }
            else
            {
                result = rule.Evaluate();
            }

            return result;
        }
        private static RuleResult GetCurrentValidationResultForRule(
            IDictionary<ValidationRule, RuleResult> ruleMap, ValidationRule rule)
        {
            lock (ruleMap)
            {
                if (!ruleMap.ContainsKey(rule))
                {
                    ruleMap.Add(rule, RuleResult.Valid());
                }

                return ruleMap[rule];
            }
        }
        private static void AddErrorsFromRuleResult(ValidationResult resultToAddTo, ValidationRule validationRule,
            RuleResult ruleResult)
        {
            if (!ruleResult.IsValid)
            {
                IEnumerable<object> errorTargets = validationRule.Target.UnwrapTargets();

                foreach (object errorTarget in errorTargets)
                {
                    foreach (string ruleError in ruleResult.Errors)
                    {
                        resultToAddTo.AddError(errorTarget, ruleError);
                    }
                }
            }
        }
        private bool ShouldExecuteOnAlreadyInvalidTarget(ValidationRule rule)
        {
            var defaultValue = Settings.DefaultRuleSettings?.ExecuteOnAlreadyInvalidTarget ?? false;

            return rule.Settings.ExecuteOnAlreadyInvalidTarget.GetValueOrDefault(defaultValue);
        }
        private async Task<bool> ExecuteRuleAsync(ValidationRule rule, ISet<object> failedTargets, ValidationResult validationResultAccumulator, SynchronizationContext syncContext)
        {
            // Skip rule if the target is already invalid and the rule is not configured to execute anyway
            if (failedTargets.Contains(rule.Target) && !ShouldExecuteOnAlreadyInvalidTarget(rule))
            {
                // Assume that the rule is valid at this point because we are not interested in this error until
                // previous rule is fixed.
                SaveRuleValidationResultAndNotifyIfNeeded(rule, RuleResult.Valid(), syncContext);

                return true;
            }

            var ruleResult = !rule.SupportsSyncValidation
                ? await rule.EvaluateAsync().ConfigureAwait(false)
                : rule.Evaluate();

            SaveRuleValidationResultAndNotifyIfNeeded(rule, ruleResult, syncContext);

            AddErrorsFromRuleResult(validationResultAccumulator, rule, ruleResult);

            if (!ruleResult.IsValid)
            {
                failedTargets.Add(rule.Target);
            }

            return true;
        }
        private static Task<RuleResult> ExecuteRuleCoreAsync(ValidationRule rule)
        {
            Task<RuleResult> resultTask;

            if (!rule.SupportsSyncValidation)
            {
                resultTask = rule.EvaluateAsync();
            }
            else
            {
                var tcs = new TaskCompletionSource<RuleResult>();

                var result = rule.Evaluate();

                tcs.SetResult(result);

                resultTask = tcs.Task;
            }

            return resultTask;
        }