/// <summary>
        /// Validates instance using DataAnnotations.
        /// </summary>
        /// <param name="instance">Validated instance.</param>
        /// <returns><see cref="FailedResult"/> if invalid, otherwise <see cref="SuccessfulResult"/>.</returns>
        public static IExecutionResult Validate(object instance)
        {
            if (instance == null)
            {
                return(new SuccessfulResult());
            }

            var validationContext = new ValidationContext(instance);
            var executionErrors   = new List <ExecutionError>();

            // Validate current level of the instance.
            CoreValidator.TryValidateObject(instance, validationContext, executionErrors, true);

            var instanceType = instance.GetType();
            var result       = new FailedResult(CoreErrorCodes.ValidationFailed, instanceType.Name);

            foreach (var executionError in executionErrors)
            {
                var field  = executionError.Source ?? string.Empty;
                var source = $"{instanceType.Name}.{field}";
                var error  = new ExecutionError(executionError.CodeInfo, source);

                result.AddError(error);
            }

            // Recursively validate nested types.
            var properties = instanceType
                             .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty)
                             .Where(PropertyCanBeValidated);

            foreach (var property in properties)
            {
                var propertyValue = property.GetValue(instance);

                if (typeof(System.Collections.IEnumerable).IsAssignableFrom(property.PropertyType))
                {
                    UpdateFailedResultWithArrayElementsDetails(result, property, instanceType, propertyValue);
                    continue;
                }

                var propertyResult = Validate(propertyValue);
                if (!(propertyResult is FailedResult fr))
                {
                    continue;
                }

                UpdateFailedResultWithDetails(result, fr, property, instanceType);
            }

            return(result.Details.Any()
                ? (IExecutionResult)result
                : new SuccessfulResult());
        }
        public void NotEmpty_ValidationFailed_ReturnsCorrectResult()
        {
            var expectedInvalidProperty = nameof(FakeInstance.Property);
            var expectedErrorMessage    = string.Format(CultureInfo.InvariantCulture, NotEmptyAttribute.ErrorMessageTemplate, expectedInvalidProperty);
            var expectedErrorCodeInfo   = new ErrorCodeInfo(AnnotationErrorCodes.NotEmpty.Code, expectedErrorMessage);
            var expectedExecutionError  = new ExecutionError(expectedErrorCodeInfo, expectedInvalidProperty, expectedErrorMessage);

            var instance = new FakeInstance();
            var context  = new ValidationContext(instance);

            var errors  = new List <ExecutionError>();
            var isValid = CoreValidator.TryValidateObject(instance, context, errors, true);

            isValid.Should().BeFalse();
            errors.Single().Should().BeEquivalentTo(expectedExecutionError);
        }