Custom Validations With Data Annotations

We have already discussed a lot about Data Annotations. There are two major ways by which we can override the convention.

  • Fluent APIs
  • Data Annotations

Note
Yes, it is a good approach to use Fluent APIs and make your code clean. But the issue is that Fluent APIs just map your Database like this:

  1. protected override void OnModelCreating(DbModelBuilder modelBuilder) {  
  2.     base.OnModelCreating(modelBuilder);  
  3.     modelBuilder.Entity<PersonalInfo>().HasKey(x => x.Id);  
  4.     modelBuilder.Entity<PersonalInfo>().Property(x => x.Age).IsRequired();  
  5.     modelBuilder.Entity<PersonalInfo>().Property(x => x.Address).IsOptional();  
  6. }  
This is how we work with Fluent API. We’ll take a complete look at Fluent APIs separately. So, I was describing how Fluent API just makes an impact on the database. It just develops your database according to what information you provide here in Fluent APIs. But on the basis of these database constraints we can’t apply the validations through Fluent APIs.

If you want to develop your Database and use validations as well then you should go ahead with Data Annotations.

Now create the PersonalInfo model class in the Model folder.

  1. namespace SMPL.Models {  
  2.     public class PersonalInfo {  
  3.         public int Id { get; set; }  
  4.         public string Name { get; set; }  
  5.         public int Age { get; set; }  
  6.         public string Phone { get; set; }  
  7.         public string Address { get; set; }  
  8.     }  
  9. }  

Now we want to allow only the members who are more than 18 years old. And by default there is no built-in data annotation in ASP.Net MVC. So we’ll create our own.

If you go to the definition of any data annotation attribute, you’ll see every attribute is inherited by ValidationAttribute class. This is the design we need to follow to make our own custom validation.

Make a new class AgeGreaterThan18 and inherit it with ValidationAttribute and here we have the IsValid function, we need to override it here.

  1. namespace SMPL.Models {  
  2.     public class AgeGreaterThan18: ValidationAttribute {  
  3.         protected override ValidationResult IsValid(object value, ValidationContext validationContext) {  
  4.             return base.IsValid(value, validationContext);  
  5.         }  
  6.     }  
  7. }  

Now, here, we have two override functions of IsValid. The first one is simple where we can directly apply the validations for that specific model property. And the second method which we’re using here also has ValidationContext through which we can access the other model properties as well of the model class.

  1. protected override ValidationResult IsValid(object value, ValidationContext validationContext) {  
  2.     var personal = (PersonalInfo) validationContext.ObjectInstance;  
  3.     if (personal.Address == null) {  
  4.         return new ValidationResult("Address Is Required");  
  5.     }  
  6.     if (personal.Name == "Usama") {  
  7.         return ValidationResult.Success;  
  8.     }  
  9.     return new ValidationResult("This Person Is Not Allowed");  
  10. }  

Here is our custom validation in a success scenario and in a failure scenario, and how it handles the user input. In the first line, we’re using Reflection to get the object instance and it is also important as well to cast this object to the class name variable. Because if you’re not casting it here, you’ll use a lot of reflection in your code which is quite cumbersome and difficult to learn and implement.

Here we’re checking the value of the property, if the Name property value is ‘Usama’ then it is ok, otherwise it is not allowed.

And now it is the time to place this attribute class on model property.

  1. public class PersonalInfo {  
  2.     public int Id { get; set; }

  3.     [Required] 
  4.     public string Name { get; set; }

  5.     [Custom] 
  6.     public int Age { get; set; }

  7.     [Required]
  8.     [DataType(DataType.PhoneNumber)]
  9.     public string Phone { get; set; }

  10.     [Required]
  11.     [StringLength(50)]
  12.     public string Address { get; set; }  
  13. }  

We’re not using value parameter in IsValid function, so we can apply this attribute on any property. With value parameter, we access the current property value where we apply it.

Custom Validation Libraries

Most of the time, we need to develop our own custom validation. So we should take an idea from different libraries or we can also use them in our projects to make our work faster otherwise we need to code the custom validations from scratch.

There are a lot of libraries for model level validations. But FoolProof and FluentValidation are most famous among them.

ValidationMessageFor and ValidationSummary

There are the HTML helpers we use in views to show the error message. ValidationMessageFor works with individual property and ValidationSummary is used to show all the errors at one place in the form.

Conclusion

My final words are if you want to use custom validations then you can make a custom class and override it with ValidationAttribute and override the IsValid function according to your requirement. Sometimes the validations are already available to us in some libraries. Library code is more tested and there is less probability that any kind of error will be there. So always prefer to use the libraries instead of writing your own code. Explore the FoolProof libraries and try to make it a replica and explore how it works. It will move your programming skills to the next level.


Similar Articles