Validation Forms In DataAnnotations

Introduction

In this second article, we will learn all validation forms in DataAnnotation. We will review Validator class with all its structure. The first part can be found on the following link.

DataAnnotation enables us to work with positive programming. In other words, it allows us anticipate exceptions for problems in our business classes or domain classes. It provides better performance. The negative programming version also exists.
  • Positive - TryValidate.
  • Negative - Validate.

On this link, you will get the information about Exceptions and Performance.

The examples class

The class for all examples will be a new version of the class created in part I.

  1. public class Customer  
  2. {  
  3.     [Required(ErrorMessage = "{0} is mandatory")]  
  4.     [MaxLength(50, ErrorMessage = "The {0} can not have more than {1} characters")]  
  5.     public string Name { get; set; }  
  6.   
  7.     [Range(typeof(DateTime), "01/01/2016""01/01/2050",  
  8.         ErrorMessage = "Valid dates for the Property {0} between {1} and {2}")]  
  9.     public DateTime EntryDate { get; set; }  
  10.   
  11.     public string Password { get; set; }  
  12.   
  13.     [Compare("Customer.Password", ErrorMessage = "The fields Password and PasswordConfirmation should be equals")]  
  14.     public string PasswordConfirmation { get; set; }  
  15.   
  16.     [Range(0, 150, ErrorMessage = "The Age should be between 0 and 150 years")]  
  17.     public int Age { get; set; }  
  18. }  
These are principal validation classes.

ValidationResult

ValidationResult is a container for class validation results. For more info, go here.

ValidationResult has 2 properties
  • ErrorMessages .- String readonly property with the information error description.
  • MemberNames.- IEnumerable<string> readonly property with the property name on error.

We have written an extension method to print the validation errors.

  1. public static string ToDescErrorsString(this IEnumerable<ValidationResult> source, string mensajeColeccionVacia = null)  
  2. {  
  3.     if (source == nullthrow new ArgumentNullException(nameof(source), $"The property {nameof(source)}, doesn't has null value");  
  4.    
  5.     StringBuilder resultado = new StringBuilder();  
  6.    
  7.     if (source.Count() > 0)  
  8.     {  
  9.         resultado.AppendLine("There are validation errors:");  
  10.         source.ToList()  
  11.             .ForEach(  
  12.                 s =>  
  13.                     resultado.AppendFormat("  {0} --> {1}{2}", s.MemberNames.FirstOrDefault(), s.ErrorMessage,  
  14.                         Environment.NewLine));  
  15.     }  
  16.     else  
  17.         resultado.AppendLine(mensajeColeccionVacia ?? string.Empty);  
  18.    
  19.     return resultado.ToString();  
  20. }  
Validator class

This is the static helper class. The Validator class allows you to execute validation on objects, properties, and methods. You can get more information from this link.

We can separate its methods in two groups, as we saw earlier.
 
POSITIVE PROGRAMMING (Returns bool value and we have a ICollection<ValidationResult> parameter)
 
The return property bool is a validation result.
The ICollection<ValidationResult> argument contains details of validation errors. If the validation doesn't have errors, this collection will be empty.

TryValidateObject
  1. public static bool TryValidateObject(object instance, ValidationContext validationContext, ICollection<ValidationResult> validationResults);  
  2. public static bool TryValidateObject(object instance, ValidationContext validationContext, ICollection<ValidationResult> validationResults, bool validateAllProperties);  
This method validates all objects. TryValidateObject has an overload with a bool argument validateAllProperties that is enabled in case when validation error continues with the validation of all properties.

Arguments
  • Instance The instance of class to validate.
  • ValidationContext The context that describes the object to validate.
  • ValidationResults The collection of validation results descriptions.
  • ValidateAllProperties Enabled continues to validate all properties.

Example

  1. public static void TryValidateObjectExample1()  
  2. {  
  3.     /// 1.- Create a customer  
  4.     var customer = new Customer  
  5.     {  
  6.         Name                 = string.Empty,  
  7.         EntryDate            = DateTime.Today,  
  8.         Password             = "AAAA",  
  9.         PasswordConfirmation = "BBBB",  
  10.         Age                  = -1  
  11.     };  
  12.     /// 2.- Create a context of validation  
  13.     ValidationContext valContext = new ValidationContext(customer, nullnull);  
  14.     /// 3.- Create a container of results  
  15.     var validationsResults = new List<ValidationResult>();  
  16.     /// 4.- Validate customer  
  17.     bool correct = Validator.TryValidateObject(customer, valContext, validationsResults, true);  
  18.    
  19.     Console.WriteLine(validationsResults.ToDescErrorsString("Without Errors !!!!"));  
  20.     Console.Read();  
  21. }  
In this example, we have done the second overload with true value (ValidateAllProperties).

Result



We have validated all customer properties.

If we change the last argument to false, it only validates the first property on error.
  1. public static void TryValidateObjectExample2()  
  2. {  
  3.     /// 1.- Create a customer  
  4.     var customer = new Customer  
  5.     {  
  6.         Name                 = string.Empty,  
  7.         EntryDate            = DateTime.Today,  
  8.         Password             = "AAAA",  
  9.         PasswordConfirmation = "BBBB",  
  10.         Age                  = -1  
  11.     };  
  12.     /// 2.- Create a context of validation  
  13.     ValidationContext valContext = new ValidationContext(customer, nullnull);  
  14.     /// 3.- Create a container of results  
  15.     var validationsResults = new List<ValidationResult>();  
  16.     /// 4.- Validate customer  
  17.     bool correct = Validator.TryValidateObject(customer, valContext, validationsResults, false);  
  18.    
  19.     Console.WriteLine(validationsResults.ToDescErrorsString("Without Errors !!!!"));  
  20.     Console.Read();  
  21. }  
Result



TryValidateProperty
  1. public static bool TryValidateProperty(object value, ValidationContext validationContext, ICollection<ValidationResult> validationResults);  
It has only one overload. It has the same parameters as of TryValidateObject, but the first object parameter does match with one property to validate and the method, therefore, validates properties.
  1. public static void TryValidatePropertyExample()  
  2. {  
  3.     /// 1.- Create a customer  
  4.     var customer = new Customer  
  5.     {  
  6.         Name                 = string.Empty,  
  7.         EntryDate            = DateTime.Today,  
  8.         Password             = "AAAA",  
  9.         PasswordConfirmation = "BBBB",  
  10.         Age                  = -1  
  11.     };  
  12.     /// 2.- Create a context of validation  
  13.     ValidationContext valContext = new ValidationContext(customer, nullnull)  
  14.     {  
  15.         MemberName = "Age"  
  16.     };  
  17.     /// 3.- Create a container of results  
  18.     var validationsResults = new List<ValidationResult>();  
  19.     /// 4.- Validate customer Age Property  
  20.     bool correct = Validator.TryValidateProperty(customer.Age, valContext, validationsResults);  
  21.    
  22.     Console.WriteLine(validationsResults.ToDescErrorsString("Without Errors !!!!"));  
  23.     Console.Read();  
  24. }  
Note

When we instance the ValidationContext object, we give value to string property MemberName with the property name to validate.

The TryValidateProperty and the TryValidateObject are equal. The only change is the first parameter. The TryValidateProperty will have to call the property value.

Result



TryValidateValue
  1. public static bool TryValidateValue(object value, ValidationContext validationContext, ICollection<ValidationResult> validationResults, IEnumerable<ValidationAttribute> validationAttributes);  
TryValidateValue validates a value through ValidationAttribute collection. This is practical to reuse our ValidationAttributes and for us to be disabused of the if terms.
  1. public static void TryValidateValueExample()  
  2. {  
  3.     /// 1.- Create value  
  4.     string myPwd = "33223";  
  5.     /// 2.- Create ValidationsAttributes  
  6.     MinLengthAttribute minLengthAtribute = new MinLengthAttribute(8) { ErrorMessage = "{0} must have {1} caracters minimum" };  
  7.     RequiredAttribute requieredAttribute = new RequiredAttribute     { ErrorMessage = "{0} is mandatory" };  
  8.     List<ValidationAttribute> atributes  = new List<ValidationAttribute>() { minLengthAtribute, requieredAttribute };  
  9.     /// 3.- Create a context of validation  
  10.     ValidationContext valContext = new ValidationContext(myPwd, nullnull)  
  11.     {  
  12.         MemberName = "myPwd"  
  13.     };  
  14.     /// 4.- Create a container of results  
  15.     var validationsResults = new List<ValidationResult>();  
  16.     /// 5.- Validate myPwd value  
  17.     bool correct = Validator.TryValidateValue(myPwd, valContext, validationsResults, atributes);  
  18.    
  19.     Console.WriteLine(validationsResults.ToDescErrorsString("Without Errors !!!!"));  
  20.     Console.Read();  
  21. }  
Note - We have created the attributes in code, because we will validate a value and we don’t have properties to mark in the class declaration.

Result


NEGATIVE PROGRAMMING (throw ValidationException and it hasn’t ICollection<ValidationResult argument )
 
 
These methods remove the word Try in your names. They are void methods. If the validation fails, it will throw a ValidationException.

They don’t have an ICollection<ValidationResult> argument. This information will have an inside exception and it isn’t a collection. But, it is a simple ValidationResult because if validation fails, it doesn’t verify the remaining properties.
  1. public static void ValidateObject(object instance, ValidationContext validationContext);  
  2. public static void ValidateObject(object instance, ValidationContext validationContext, bool validateAllProperties);  
  3. public static void ValidateProperty(object value, ValidationContext validationContext);  
  4. public static void ValidateValue(object value, ValidationContext validationContext, IEnumerable<ValidationAttribute> validationAttributes);  
Example
  1. public static void ValidateObjectExample()  
  2. {  
  3.     /// 1.- Create a customer  
  4.     var customer = new Customer  
  5.     {  
  6.         Name                 = string.Empty,  
  7.         EntryDate            = DateTime.Today,  
  8.         Password             = "AAAA",  
  9.         PasswordConfirmation = "BBBB",  
  10.         Age                  = -1  
  11.     };  
  12.     /// 2.- Create a context of validation  
  13.     ValidationContext valContext = new ValidationContext(customer, nullnull);  
  14.    
  15.     try  
  16.     {  
  17.         /// 3.- Validate customer  
  18.         Validator.ValidateObject(customer, valContext, true);  
  19.     }  
  20.     catch (ValidationException ex)  
  21.     {  
  22.         /// 4.- Print Validations Results  
  23.         ValidationResult s = ex.ValidationResult;  
  24.    
  25.         Console.WriteLine("There are validation errors:");  
  26.         Console.WriteLine("  {0,-20} --> {1}{2}", s.MemberNames.FirstOrDefault(), s.ErrorMessage,  
  27.                         Environment.NewLine);  
  28.     }  
  29.    
  30.               
  31.     Console.Read();  
  32. }  
Result



The other methods are same as the ‘Try’ version.

Considerations and tests

Remember, we can use the extension method of Chapter I.

You have the source code to test the examples.