Esempio n. 1
0
        /// <summary>
        ///   Starts a validation asynchronously and returns a Task for the operation.
        /// </summary>
        /// <typeparam name="TTarget"> The type of the object to validate. </typeparam>
        /// <param name="plan"> The validation plan to execute asynchronously. </param>
        /// <param name="target"> The type of the object to validate. </param>
        /// <returns> </returns>
        public static Task <ValidationReport> ExecuteAsync <TTarget>(
            this ValidationPlan <TTarget> plan,
            TTarget target)
        {
            var tcs   = new TaskCompletionSource <ValidationReport>(target);
            var scope = new ValidationScope(enter: false);

            Task.Factory.StartNew(() =>
            {
                var taskDictionary =
                    new ConcurrentDictionary <IValidationRule <TTarget>, Task <bool> >();

                // create and start a task for each rule
                plan.Select(
                    rule => taskDictionary.GetOrAdd(rule,
                                                    rule.ToTask(target,
                                                                taskDictionary,
                                                                scope,
                                                                TaskCreationOptions.AttachedToParent)))
                .ForEach(t => t.TryStart());
            })
            .ContinueWith(t =>
            {
                try
                {
                    tcs.Complete(t, () => ValidationReport.FromScope(scope));
                }
                finally
                {
                    scope.Dispose();
                }
            });
            return(tcs.Task);
        }
Esempio n. 2
0
 /// <summary>
 ///   Determines whether the specified target is valid.
 /// </summary>
 /// <param name="target"> The target. </param>
 /// <returns> <c>true</c> if the specified target is valid; otherwise, <c>false</c> . </returns>
 public bool Check(TTarget target)
 {
     using (var scope = new ValidationScope {
         Rule = this
     })
     {
         return(Check(target, scope));
     }
 }
Esempio n. 3
0
        /// <summary>
        ///   Determines (synchronously) whether the specified target is valid.
        /// </summary>
        /// <param name="target"> The object to be validated. </param>
        /// <param name="scope"> The <see cref="ValidationScope" /> in which to perform the validation. </param>
        /// <returns> <c>true</c> if the specified target is valid; otherwise, <c>false</c> . </returns>
        public override bool Check(TTarget target, ValidationScope scope)
        {
            var task = TaskFor(target, scope).TryStart();

            if (task.Status == TaskStatus.WaitingForActivation)
            {
                throw new InvalidOperationException("The rule cannot be evaluated because its task is dependent on another task that has not been started.");
            }
            return(task.Result);
        }
Esempio n. 4
0
 /// <summary>
 ///   Performs the actual rule check.
 /// </summary>
 protected override bool PerformCheck(TTarget target, ValidationScope scope = null)
 {
     using (scope = new ValidationScope(scope)
     {
         Rule = this
     })
     {
         return(scope.HaltsOnFirstFailure
                    ? EvaluationStrategy <TTarget> .HaltOnFirstFailure.Evaluate(this, target, scope)
                    : Strategy.Evaluate(this, target, scope));
     }
 }
Esempio n. 5
0
        /// <summary>
        ///   Exit the specified <paramref name="scope" /> .
        /// </summary>
        /// <remarks>
        ///   If there are nested scopes within the specified <paramref name="scope" /> , those will be exited as well.
        /// </remarks>
        /// <param name="scope"> The scope to exit </param>
        protected static void ExitScope(ValidationScope scope)
        {
            if (!ActiveScopes.Contains(scope))
            {
                return;
            }

            ValidationScope popped;

            do
            {
                popped = ActiveScopes.Pop();
            } while (scope != popped);
        }
Esempio n. 6
0
        /// <summary>
        ///   Determines whether the specified target is valid.
        /// </summary>
        /// <param name="target"> The target. </param>
        /// <param name="scope"> The ValidationScope for the operation. </param>
        /// <returns> <c>true</c> if the specified target is valid; otherwise, <c>false</c> . </returns>
        public virtual bool Check(TTarget target, ValidationScope scope)
        {
            // when there is no active ValidationScope, simply perform the check
            if (scope == null)
            {
                if (preconditions != null && preconditions.Any(rule => !rule.Check(target)))
                {
                    // this rule is considered to have succeeded, i.e. not failed, because it has not been evaluated, so return true:
                    return(true);
                }
                return(PerformCheck(target, scope));
            }

            if (CheckPreconditions(scope.Parent ?? scope, target))
            {
                return(true);
            }

            // get the result of the rule
            var result = PerformCheck(target, scope);

            var messageGenerator = MessageGenerator ?? scope.MessageGenerator;

            // extract paramaters from the scope and store them if needed in a FailedEvaluation
            var parameters = scope.FlushParameters();

            if (!result)
            {
                // record the failure in the ValidationScope
                scope.AddEvaluation(new FailedEvaluation(target, this, messageGenerator)
                {
                    IsInternal = CreatesEvaluationsAsInternal,
                    Parameters = parameters
                });
            }
            else
            {
                scope.AddEvaluation(new SuccessfulEvaluation(target, this, messageGenerator)
                {
                    IsInternal = CreatesEvaluationsAsInternal,
                    Parameters = parameters
                });
            }

            return(result);
        }
Esempio n. 7
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="ValidationScope" /> class.
        /// </summary>
        public ValidationScope(bool enter = true)
        {
            if (activeScopes != null && activeScopes.Count > 0)
            {
                parent      = ActiveScopes.Peek();
                evaluations = parent.evaluations;
            }
            else
            {
                evaluations = new ConcurrentBag <RuleEvaluation>();
            }

            if (enter)
            {
                EnterScope(this);
            }
        }
Esempio n. 8
0
 private Func <TTarget, ValidationScope, Task <bool> > CreateTask(Func <TTarget, Task <TTarget> > setup, Func <TTarget, bool> validate) =>
 (target, parentScope) =>
 {
     var tcs  = new TaskCompletionSource <bool>();
     var task = setup(target);
     task.ContinueWith(
         t => tcs.Complete(t,
                           () =>
     {
         using (var scope = new ValidationScope(parentScope))
         {
             var result = validate(t.Result);
             RecordResult(scope, result, target);
             return(result);
         }
     }));
     return(tcs.Task);
 };
Esempio n. 9
0
        public Task <bool> TaskFor(TTarget target, ValidationScope scope)
        {
            var tcs = new TaskCompletionSource <bool>();

            Task.Factory
            .StartNew(() =>
            {
                // TODO: (TaskFor) prevent setup from running if a precondition failed. IsPreconditionUnsatisfied may not be appropriate for async since it forces evaluation when it finds an unevaluated precondition.
                // if (CheckPreconditions(scope, target))
                // {
                //     tcs.SetCanceled();
                //     return;
                // }

                taskFactory(target, scope).TryStart();
            });

            return(tcs.Task);
        }
Esempio n. 10
0
        /// <summary>
        ///   Evaluates all constituent validation rules against the specified target.
        /// </summary>
        /// <param name="target"> The target. </param>
        /// <param name="haltOnFirstFailure"> When true, stops execution on the first failed rule </param>
        /// <returns> </returns>
        public ValidationReport Execute(TTarget target, bool haltOnFirstFailure = false)
        {
            using (var scope = new ValidationScope {
                MessageGenerator = MessageGenerator, Rule = this
            })
            {
                if (haltOnFirstFailure)
                {
                    scope.HaltsOnFirstFailure = true;
                }

                foreach (var rule in rules)
                {
                    var ruleTemp = rule;
                    if (!scope.HasFailed(target, ruleTemp))
                    {
                        var success = rule.Check(target);

                        // ValidationRule<TTarget> handles this internally but otherwise add it to the scope.
                        if (!(rule is ValidationRule <TTarget>))
                        {
                            if (!success)
                            {
                                scope.AddEvaluation(new FailedEvaluation(target, rule, MessageGenerator));
                            }
                            else
                            {
                                scope.AddEvaluation(new SuccessfulEvaluation(target, rule, MessageGenerator));
                            }
                        }
                    }

                    if (scope.ShouldHalt())
                    {
                        break;
                    }
                }

                return(ValidationReport.FromScope(scope));
            }
        }
Esempio n. 11
0
        private void RecordResult(ValidationScope scope, bool result, TTarget target)
        {
            var parameters = scope.FlushParameters();

            var messageGenerator = scope.MessageGenerator;

            if (result)
            {
                scope.AddEvaluation(new SuccessfulEvaluation(target, this)
                {
                    MessageGenerator = messageGenerator,
                    Parameters       = parameters
                });
            }
            else
            {
                scope.AddEvaluation(new FailedEvaluation(target, this)
                {
                    MessageGenerator = messageGenerator,
                    Parameters       = parameters
                });
            }
        }
Esempio n. 12
0
        private static Task <bool> ToTask <T>(
            this IValidationRule <T> forRule,
            T target,
            ConcurrentDictionary <IValidationRule <T>, Task <bool> > tasks,
            ValidationScope scope,
            TaskCreationOptions options = TaskCreationOptions.PreferFairness)
        {
            // if the rule is inherently capable of async, let it build the task
            var asyncRule = forRule as AsyncValidationRule <T>;

            if (asyncRule != null)
            {
                return(asyncRule.TaskFor(target, scope));
            }

            // otherwise, build a task from its tree
            var task = new Task <bool>(
                () =>
            {
                var rule = forRule as ValidationRule <T>;

                // there are preconditions, so each will need its own task, and main task must wait on them all
                rule?.preconditions
                .ForEach(pre => tasks.GetOrAdd(rule,
                                               _ => pre.ToTask(target,
                                                               tasks,
                                                               scope,
                                                               TaskCreationOptions.AttachedToParent)));

                using (var innerScope = new ValidationScope(scope))
                {
                    return(forRule.Check(target, innerScope));
                }
            }, options);

            return(task);
        }
Esempio n. 13
0
 internal static ValidationReport FromScope(ValidationScope scope) => new ValidationReport(scope.Evaluations);
Esempio n. 14
0
 /// <summary>
 ///   Enters the specified <paramref name="scope" /> .
 /// </summary>
 /// <param name="scope"> The scope. </param>
 protected static void EnterScope(ValidationScope scope)
 {
     ActiveScopes.Push(scope);
 }
Esempio n. 15
0
 /// <summary>
 ///   Initializes a new instance of the <see cref="ValidationScope" /> class.
 /// </summary>
 /// <param name="parent"> The parent. </param>
 public ValidationScope(ValidationScope parent)
 {
     this.parent = parent;
     evaluations = parent.evaluations;
     EnterScope(this);
 }
Esempio n. 16
0
 /// <summary>
 ///   Performs the actual rule check.
 /// </summary>
 protected virtual bool PerformCheck(TTarget target, ValidationScope scope = null) =>
 condition(target);
Esempio n. 17
0
        protected bool CheckPreconditions(ValidationScope scope, TTarget target)
        {
            if (preconditions == null)
            {
                return(false);
            }

            // check previously-evaluated preconditions. any occurrence of the same combination of rule and target will short circuit the current operation.
            if (scope.AllFailures.Any(f =>
                                      preconditions.Any(pre =>
                                                        Equals(f.Target, target) &&
                                                        ValidationRuleComparer.Instance.Equals(pre, f.Rule))))
            {
                // this failure indicates a short-circuited precondition. this mechanism identifies preconditions of preconditions.
                // TODO: (CheckPreconditions) mark the failure so that it can be identified as such for debug purposes
                scope.AddEvaluation(new FailedEvaluation(target, this)
                {
                    IsInternal = true
                });
                return(true);
            }

            // check unevaluated preconditions
            foreach (var rule in preconditions)
            {
                var tempRule = rule;
                if (!scope.Evaluations.Any(ex => Equals(ex.Target, target) &&
                                           ValidationRuleComparer.Instance.Equals(ex.Rule, tempRule)))
                {
                    using (var internalScope = new ValidationScope {
                        Rule = this
                    })
                    {
                        internalScope.RuleEvaluated += (s, e) =>
                        {
                            var failure = e.RuleEvaluation as FailedEvaluation;
                            if (failure != null)
                            {
                                failure.IsInternal = true;
                            }
                        };

                        if (!rule.Check(target))
                        {
                            return(true);
                        }
                    }

                    // finally we have to check if the rule's preconditions were in turn unsatisfied.
                    var vRule = rule as ValidationRule <TTarget>;
                    if (vRule != null)
                    {
                        if (vRule.CheckPreconditions(scope, target))
                        {
                            return(true);
                        }
                    }
                }
            }
            return(false);
        }
 public virtual bool Evaluate(IEnumerable <IValidationRule <T> > rules, T target, ValidationScope scope) =>
 evaluate(rules, target, scope);