Why doesn't Validator.TryValidateObject validate the class if I have a validation attribute in the property?

I created a custom ValidationAttribute attribute that is for the class. This is correctly checked whenever I try to call Validator.TryValidateObject. But when I have another ValidationAttribute parameter in the properties inside my class, the test results do not contain the result for checking the class level.

Here is a sample code:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public class IsHelloWorldAttribute : ValidationAttribute { public object _typeId = new object(); public string FirstProperty { get; set; } public string SecondProperty { get; set; } public IsHelloWorldAttribute(string firstProperty, string secondProperty) { this.FirstProperty = firstProperty; this.SecondProperty = secondProperty; } public override bool IsValid(object value) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value); string str1 = properties.Find(FirstProperty, true).GetValue(value) as string; string str2 = properties.Find(SecondProperty, true).GetValue(value) as string; if (string.Format("{0}{1}", str1,str2) == "HelloWorld") return true; return false; } public override object TypeId { get { return _typeId; } } } 

Here is the class code I need to check

 [IsHelloWorld("Name", "Code", ErrorMessage="Is not Hello World")] public class MyViewModel : BaseViewModel { string name; string code; [Required] public string Name { get { return model.Name; } set { if (model.Name != value) { model.Name = value; base.RaisePropertyChanged(() => this.Name); } } } public string Code { get { return code; } set { if (code != value) { code = value; base.RaisePropertyChanged(() => this.Code); } } } } 

This is how I call the TryValidateObject method:

  var validationContext = new ValidationContext(this, null, null); var validationResults = new List<ValidationResult>(); Validator.TryValidateObject(this, validationContext, validationResults, true); 

Now, if I have the [Required] attribute in the Name property, and I tried to call Validator.TryValidateObject, the result of the check will be only one, which is the result for the Required check. But when I removed the [Required] attribute from the name and left the IsHellowWorld attribute and then called the TryValidateObject, it will give me one result and the result of HellowWorldValidation.

I need to do all the checks at the class level and at the property level. Can I achieve this without implementing my TryValidateObject method?

+7
c # validation annotations
source share
1 answer

This is because a short circuit is checked if property errors are detected. This makes sense since class level checking can be more expensive, potentially involving callbacks, other data source calls, etc.

If a property error has been detected, then the logic in its current form is simply reduced.

Inside System.ComponentModel.DataAnnotations.Validator:

  public static bool TryValidateObject(object instance, ValidationContext validationContext, ICollection<ValidationResult> validationResults, bool validateAllProperties) { if (instance == null) throw new ArgumentNullException("instance"); if (validationContext != null && instance != validationContext.ObjectInstance) throw new ArgumentException(DataAnnotationsResources.Validator_InstanceMustMatchValidationContextInstance, "instance"); bool flag = true; bool breakOnFirstError = validationResults == null; foreach (Validator.ValidationError validationError in Validator.GetObjectValidationErrors(instance, validationContext, validateAllProperties, breakOnFirstError)) { flag = false; if (validationResults != null) validationResults.Add(validationError.ValidationResult); } return flag; } 

Pay attention to the call Validator.GetObjectValidationErrors , which in turn is defined as:

  private static IEnumerable<Validator.ValidationError> GetObjectValidationErrors(object instance, ValidationContext validationContext, bool validateAllProperties, bool breakOnFirstError) { if (instance == null) throw new ArgumentNullException("instance"); if (validationContext == null) throw new ArgumentNullException("validationContext"); List<Validator.ValidationError> list = new List<Validator.ValidationError>(); //Check for property errors here list.AddRange(Validator.GetObjectPropertyValidationErrors(instance, validationContext, validateAllProperties, breakOnFirstError)); // Short circuits here if any found if (Enumerable.Any<Validator.ValidationError>((IEnumerable<Validator.ValidationError>) list)) return (IEnumerable<Validator.ValidationError>) list; // Class level validation occurs below this point IEnumerable<ValidationAttribute> validationAttributes = Validator._store.GetTypeValidationAttributes(validationContext); list.AddRange(Validator.GetValidationErrors(instance, validationContext, validationAttributes, breakOnFirstError)); if (Enumerable.Any<Validator.ValidationError>((IEnumerable<Validator.ValidationError>) list)) return (IEnumerable<Validator.ValidationError>) list; IValidatableObject validatableObject = instance as IValidatableObject; if (validatableObject != null) { foreach (ValidationResult validationResult in Enumerable.Where<ValidationResult>(validatableObject.Validate(validationContext), (Func<ValidationResult, bool>) (r => r != ValidationResult.Success))) list.Add(new Validator.ValidationError((ValidationAttribute) null, instance, validationResult)); } return (IEnumerable<Validator.ValidationError>) list; } 
+4
source share

All Articles