public static IAsyncValidationRule AddChildValidatable([NotNull] this ValidationHelper validator,
                                                               [NotNull] Expression <Func <IValidatable> > childValidatableGetter)
        {
            Guard.NotNull(validator, nameof(validator));
            Guard.NotNull(childValidatableGetter, nameof(childValidatableGetter));

            Func <IValidatable> getter = childValidatableGetter.Compile();

            return(validator.AddAsyncRule(PropertyName.For(childValidatableGetter), () =>
            {
                IValidatable validatable = getter();

                if (validatable != null)
                {
                    return validatable.Validate().ContinueWith(r =>
                    {
                        ValidationResult result = r.Result;

                        var ruleResult = new RuleResult();

                        foreach (ValidationError error in result.ErrorList)
                        {
                            ruleResult.AddError(error.ErrorText);
                        }

                        return ruleResult;
                    });
                }

                return TaskEx.FromResult(RuleResult.Valid());
            }));
        }
        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 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 ValidationResult ExecuteValidationRules(IEnumerable <ValidationRule> rulesToExecute, SynchronizationContext syncContext = null)
        {
            var result = new ValidationResult();

            var failedTargets = new HashSet <object>();

            foreach (ValidationRule validationRule in rulesToExecute)
            {
                // Skip rule if the target is already invalid
                if (failedTargets.Contains(validationRule.Target))
                {
                    // Assume that the rule is valid at this point because we are not interested in this error until
                    // previous rule is fixed.
                    SaveRuleValidationResultAndNotifyIfNeeded(validationRule, RuleResult.Valid(), syncContext);

                    continue;
                }

                RuleResult ruleResult = ExecuteRuleCore(validationRule);

                SaveRuleValidationResultAndNotifyIfNeeded(validationRule, ruleResult, syncContext);

                AddErrorsFromRuleResult(result, validationRule, ruleResult);

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

            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]);
            }
        }
        public static IValidationRule AddRequiredRule([NotNull] this ValidationHelper validator,
                                                      [NotNull] Expression <Func <object> > propertyExpression, [NotNull] string errorMessage)
        {
            Guard.NotNull(validator, nameof(validator));
            Guard.NotNull(propertyExpression, nameof(propertyExpression));
            Guard.NotNullOrEmpty(errorMessage, nameof(errorMessage));

            Func <object> propertyGetter = propertyExpression.Compile();

            return(validator.AddRule(PropertyName.For(propertyExpression, false), () =>
            {
                object propertyValue = propertyGetter();

                var stringPropertyValue = propertyValue as string;

                if (propertyValue == null || (stringPropertyValue != null && string.IsNullOrEmpty(stringPropertyValue)))
                {
                    return RuleResult.Invalid(errorMessage);
                }

                return RuleResult.Valid();
            }));
        }
        public static IAsyncValidationRule AddChildValidatable([NotNull] this ValidationHelper validator,
                                                               [NotNull] Expression <Func <IValidatable> > childValidatableGetter)
        {
            Contract.Requires(validator != null);
            Contract.Requires(childValidatableGetter != null);
            Contract.Ensures(Contract.Result <IAsyncValidationRule>() != null);

            Func <IValidatable> getter = childValidatableGetter.Compile();

            return(validator.AddAsyncRule(PropertyName.For(childValidatableGetter), () =>
            {
                IValidatable validatable = getter();

                if (validatable != null)
                {
#if SILVERLIGHT_4
                    validatable.Validate(result =>
                    {
#else
                    return validatable.Validate().ContinueWith(r =>
                    {
                        ValidationResult result = r.Result;
#endif
                        var ruleResult = new RuleResult();

                        foreach (ValidationError error in result.ErrorList)
                        {
                            ruleResult.AddError(error.ErrorText);
                        }

                        return ruleResult;
                    });
                }

                return Task.Factory.StartNew(() => RuleResult.Valid());
            }));
        }
        private Task <bool> ExecuteNextRuleFromQueueAsync(Queue <ValidationRule> rulesQueue, ISet <object> failedTargets,
                                                          ValidationResult validationResultAccumulator, SynchronizationContext syncContext)
        {
            if (rulesQueue.Count == 0)
            {
                return(TaskEx.FromResult(false));
            }

            var rule = rulesQueue.Dequeue();

            // Skip rule if the target is already invalid
            if (failedTargets.Contains(rule.Target))
            {
                // 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(TaskEx.FromResult(true));
            }

            return(ExecuteRuleCoreAsync(rule).ContinueWith(t =>
            {
                RuleResult ruleResult = t.Result;

                SaveRuleValidationResultAndNotifyIfNeeded(rule, ruleResult, syncContext);

                AddErrorsFromRuleResult(validationResultAccumulator, rule, ruleResult);

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

                return true;
            }));
        }
        public static IValidationRule AddRequiredRule([NotNull] this ValidationHelper validator,
                                                      [NotNull] Expression <Func <object> > propertyExpression, [NotNull] string errorMessage)
        {
            Contract.Requires(validator != null);
            Contract.Requires(propertyExpression != null);
            Contract.Requires(!string.IsNullOrEmpty(errorMessage));
            Contract.Ensures(Contract.Result <IValidationRule>() != null);

            Func <object> propertyGetter = propertyExpression.Compile();

            return(validator.AddRule(propertyExpression, () =>
            {
                object propertyValue = propertyGetter();

                var stringPropertyValue = propertyValue as string;

                if (propertyValue == null || (stringPropertyValue != null && string.IsNullOrEmpty(stringPropertyValue)))
                {
                    return RuleResult.Invalid(errorMessage);
                }

                return RuleResult.Valid();
            }));
        }
        public static IAsyncValidationRule AddChildValidatableCollection([NotNull] this ValidationHelper validator,
                                                                         [NotNull] Expression <Func <IEnumerable <IValidatable> > > validatableCollectionGetter)
        {
            Contract.Requires(validator != null);
            Contract.Requires(validatableCollectionGetter != null);
            Contract.Ensures(Contract.Result <IAsyncValidationRule>() != null);

            Func <IEnumerable <IValidatable> > getter = validatableCollectionGetter.Compile();

            return(validator.AddAsyncRule(PropertyName.For(validatableCollectionGetter), () =>
            {
                IEnumerable <IValidatable> items = getter();

                if (items == null)
                {
                    return Task.Factory.StartNew(() => RuleResult.Valid());
                }

                return Task.Factory.StartNew(() =>
                {
                    var result = new RuleResult();

                    // Execute validation on all items at the same time, wait for all
                    // to fininish and combine the results.

                    var results = new List <ValidationResult>();

                    var syncEvent = new ManualResetEvent(false);

                    var itemsArray = items as IValidatable[] ?? items.ToArray();
                    int[] numerOfThreadsNotYetCompleted = { itemsArray.Length };

                    foreach (var item in itemsArray)
                    {
#if SILVERLIGHT_4
                        item.Validate(r =>
                        {
                            Exception ex = null;
#else
                        item.Validate().ContinueWith(tr =>
                        {
                            ValidationResult r = tr.Result;
                            AggregateException ex = tr.Exception;
#endif
                            lock (results)
                            {
                                // ReSharper disable ConditionIsAlwaysTrueOrFalse
                                if (ex == null && r != null)
                                // ReSharper restore ConditionIsAlwaysTrueOrFalse
                                {
                                    results.Add(r);
                                }

                                if (Interlocked.Decrement(ref numerOfThreadsNotYetCompleted[0]) == 0)
                                {
                                    syncEvent.Set();
                                }
                            }
                        });
                    }

                    if (numerOfThreadsNotYetCompleted[0] > 0)
                    {
                        // Wait for all items to finish validation
                        syncEvent.WaitOne();

                                          // Add errors from all validation results
                                          foreach (ValidationResult itemResult in results)
                        {
                            foreach (ValidationError error in itemResult.ErrorList)
                            {
                                result.AddError(error.ErrorText);
                            }
                        }
                    }

                    return result;
                });
            }));
        }
        public static IAsyncValidationRule AddChildValidatableCollection([NotNull] this ValidationHelper validator,
                                                                         [NotNull] Expression <Func <IEnumerable <IValidatable> > > validatableCollectionGetter)
        {
            Guard.NotNull(validator, nameof(validator));
            Guard.NotNull(validatableCollectionGetter, nameof(validatableCollectionGetter));

            Func <IEnumerable <IValidatable> > getter = validatableCollectionGetter.Compile();

            return(validator.AddAsyncRule(PropertyName.For(validatableCollectionGetter), () =>
            {
                IEnumerable <IValidatable> items = getter();

                if (items == null)
                {
                    return TaskEx.FromResult(RuleResult.Valid());
                }

                items = items as IValidatable[] ?? items.ToArray();

                if (!items.Any())
                {
                    return TaskEx.FromResult(RuleResult.Valid());
                }

                var result = new RuleResult();

                // Execute validation on all items at the same time, wait for all
                // to finish and combine the results.

                var results = new List <ValidationResult>();

                var tasks = new List <Task <ValidationResult> >();

                foreach (var item in items)
                {
                    var task = item.Validate().ContinueWith(tr =>
                    {
                        ValidationResult r = tr.Result;
                        AggregateException ex = tr.Exception;

                        lock (results)
                        {
                            if (ex == null && r != null)
                            {
                                results.Add(r);
                            }
                        }

                        return tr.Result;
                    });

                    tasks.Add(task);
                }

                var resultTask = TaskEx.WhenAll(tasks).ContinueWith(tr =>
                {
                    if (tr.Exception == null)
                    {
                        // Add errors from all validation results
                        foreach (ValidationResult itemResult in results)
                        {
                            foreach (ValidationError error in itemResult.ErrorList)
                            {
                                result.AddError(error.ErrorText);
                            }
                        }

                        return result;
                    }

                    throw new AggregateException(tr.Exception);
                });

                return resultTask;
            }));
        }