diff --git a/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationValidator.cs b/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationValidator.cs index 900230afcf..3333354937 100644 --- a/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationValidator.cs +++ b/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationValidator.cs @@ -10,53 +10,61 @@ namespace Volo.Abp.Validation { public void Validate(object validatingObject) { - var validationResult = new AbpValidationResult(); + var errors = GetErrors(validatingObject); - AddErrors(validationResult, validatingObject); - - if (validationResult.Errors.Any()) + if (errors.Any()) { throw new AbpValidationException( "Object state is not valid! See ValidationErrors for details.", - validationResult.Errors + errors ); } } /// - /// Checks all properties for DataAnnotations attributes. + /// Gets all errors from properties for DataAnnotations attributes and IValidatableObject interface. /// - public virtual void AddErrors(IAbpValidationResult validationResult, object validatingObject) + public virtual List GetErrors(object validatingObject) { + var errors = new List(); var properties = TypeDescriptor.GetProperties(validatingObject).Cast(); + foreach (var property in properties) { - var validationAttributes = property.Attributes.OfType().ToArray(); - if (validationAttributes.IsNullOrEmpty()) - { - continue; - } + AddPropertyErrors(validatingObject, property, errors); + } - var validationContext = new ValidationContext(validatingObject) - { - DisplayName = property.DisplayName, - MemberName = property.Name - }; + if (validatingObject is IValidatableObject validatableObject) + { + errors.AddRange( + validatableObject.Validate(new ValidationContext(validatableObject)) + ); + } - foreach (var attribute in validationAttributes) - { - var result = attribute.GetValidationResult(property.GetValue(validatingObject), validationContext); - if (result != null) - { - validationResult.Errors.Add(result); - } - } + return errors; + } + + protected virtual void AddPropertyErrors(object validatingObject, PropertyDescriptor property, List errors) + { + var validationAttributes = property.Attributes.OfType().ToArray(); + if (validationAttributes.IsNullOrEmpty()) + { + return; } - if (validatingObject is IValidatableObject) + var validationContext = new ValidationContext(validatingObject) { - var results = (validatingObject as IValidatableObject).Validate(new ValidationContext(validatingObject)); - validationResult.Errors.AddRange(results); + DisplayName = property.DisplayName, + MemberName = property.Name + }; + + foreach (var attribute in validationAttributes) + { + var result = attribute.GetValidationResult(property.GetValue(validatingObject), validationContext); + if (result != null) + { + errors.Add(result); + } } } } diff --git a/src/Volo.Abp.Validation/Volo/Abp/Validation/IDataAnnotationValidator.cs b/src/Volo.Abp.Validation/Volo/Abp/Validation/IDataAnnotationValidator.cs index a31642daae..6b14ff66b6 100644 --- a/src/Volo.Abp.Validation/Volo/Abp/Validation/IDataAnnotationValidator.cs +++ b/src/Volo.Abp.Validation/Volo/Abp/Validation/IDataAnnotationValidator.cs @@ -1,9 +1,12 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + namespace Volo.Abp.Validation { public interface IDataAnnotationValidator { void Validate(object validatingObject); - void AddErrors(IAbpValidationResult validationResult, object validatingObject); + List GetErrors(object validatingObject); } } \ No newline at end of file diff --git a/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs b/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs index ee501a44de..8388605e62 100644 --- a/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs +++ b/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs @@ -1,9 +1,16 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + namespace Volo.Abp.Validation { public interface IObjectValidator { - void Validate(object validatingObject, string name = null, bool allowNull = false); + void Validate( + object validatingObject, + string name = null, + bool allowNull = false + ); - void AddErrors(IAbpValidationResult validationResult, object validatingObject, string name = null, bool allowNull = false); + List GetErrors(object validatingObject, string name = null, bool allowNull = false); } } \ No newline at end of file diff --git a/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs b/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs index aa4dab2aa1..bf266b3936 100644 --- a/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs +++ b/src/Volo.Abp.Validation/Volo/Abp/Validation/MethodInvocationValidator.cs @@ -40,6 +40,7 @@ namespace Volo.Abp.Validation throw new Exception("Method parameter count does not match with argument count!"); } + //todo: consider to remove this condition if (context.Errors.Any() && HasSingleNullArgument(context)) { ThrowValidationError(context); @@ -90,7 +91,7 @@ namespace Volo.Abp.Validation parameterInfo.IsOut || TypeHelper.IsPrimitiveExtendedIncludingNullable(parameterInfo.ParameterType, includeEnums: true); - _objectValidator.AddErrors(context, parameterValue, parameterInfo.Name, allowNulls); + context.Errors.AddRange(_objectValidator.GetErrors(parameterValue, parameterInfo.Name, allowNulls)); } } } \ No newline at end of file diff --git a/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs b/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs index 1f1062c993..315652b78c 100644 --- a/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs +++ b/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -8,7 +9,7 @@ using Volo.Abp.Reflection; namespace Volo.Abp.Validation { - public class ObjectValidator : ITransientDependency, IObjectValidator + public class ObjectValidator : IObjectValidator, ITransientDependency { private const int MaxRecursiveParameterValidationDepth = 8; @@ -23,36 +24,38 @@ namespace Volo.Abp.Validation public virtual void Validate(object validatingObject, string name = null, bool allowNull = false) { - var validationResult = new AbpValidationResult(); + var errors = GetErrors(validatingObject, name, allowNull); - AddErrors(validationResult, validatingObject, name, allowNull); - - if (validationResult.Errors.Any()) + if (errors.Any()) { throw new AbpValidationException( "Object state is not valid! See ValidationErrors for details.", - validationResult.Errors + errors ); } } - public virtual void AddErrors(IAbpValidationResult validationResult, object validatingObject, string name = null, bool allowNull = false) + public virtual List GetErrors(object validatingObject, string name = null, bool allowNull = false) { + var errors = new List(); + if (validatingObject == null && !allowNull) { - validationResult.Errors.Add( + errors.Add( name == null ? new ValidationResult("Given object is null!") : new ValidationResult(name + " is null!", new[] { name }) ); - return; + return errors; } - ValidateObjectRecursively(validationResult, validatingObject, 1); + ValidateObjectRecursively(errors, validatingObject, currentDepth: 1); + + return errors; } - protected virtual void ValidateObjectRecursively(IAbpValidationResult context, object validatingObject, int currentDepth) + protected virtual void ValidateObjectRecursively(List errors, object validatingObject, int currentDepth) { if (currentDepth > MaxRecursiveParameterValidationDepth) { @@ -64,20 +67,19 @@ namespace Volo.Abp.Validation return; } - _dataAnnotationValidator.AddErrors(context, validatingObject); + errors.AddRange(_dataAnnotationValidator.GetErrors(validatingObject)); //Validate items of enumerable - if (validatingObject is IEnumerable && !(validatingObject is IQueryable)) + if (validatingObject is IEnumerable) { - foreach (var item in (validatingObject as IEnumerable)) + if (!(validatingObject is IQueryable)) { - ValidateObjectRecursively(context, item, currentDepth + 1); + foreach (var item in (validatingObject as IEnumerable)) + { + ValidateObjectRecursively(errors, item, currentDepth + 1); + } } - } - //Do not recursively validate for enumerable objects - if (validatingObject is IEnumerable) - { return; } @@ -102,7 +104,7 @@ namespace Volo.Abp.Validation continue; } - ValidateObjectRecursively(context, property.GetValue(validatingObject), currentDepth + 1); + ValidateObjectRecursively(errors, property.GetValue(validatingObject), currentDepth + 1); } } }