Introduction
When developing a custom validation attribute I always prefer to use it for multiple purposes, otherwise it might be duplicated. Date validation is very important in real projects since it includes a lot of business validation logic. In this article, I will try to show you how to implement multiple validations at the server side in MVC using ASP.NET Data Annotations.
Requirement
There might be many requirements to validate a Date object. It might be required that it be in the correct format, it might be that the end date must be greater than the start date and so on. MVC provides RequiredValidator, RegularExpressionValidator, CompareValidator and so on. But if you have specific cases like the minimum date should not be less than 2014 and so on then we need to create our Custom Validation Classes. What if sometimes it requires multiple validations based on requirements? Will you create 4-5 custom validators? I am trying to address this problem.
Basics
- Let us first create an enum:
- public enum ValidationType
- {
- RangeValidation,
- Compare
- }
- Now we will create a custom validator used for multiple validations. For that we need to inherit it from the System.ComponentModel.DataAnnotations.ValidationAttribute as in the following:
- [AttributeUsage(AttributeTargets.Property,AllowMultiple=true,Inherited=true)]
- public class DateValidationAttribute : ValidationAttribute
- {}
The AllowMultiple property of AttributeUsage is the key to enable multiple validations.
Solution
Let's see my implementation of the custom validator:
- private ValidationType _validationType;
- private DateTime? _fromDate;
- private DateTime _toDate;
- private string _defaultErrorMessage;
- private string _propertyNameToCompare;
-
- public DateValidationAttribute(ValidationType validationType, string message, string compareWith = "", string fromDate= "")
- {
- _validationType = validationType;
- switch (validationType)
- {
- switch (validationType)
- {
- case ValidationType.Compare:
- {
- _propertyNameToCompare = compareWith;
- _defaultErrorMessage = message;
- break;
- }
-
- case ValidationType.RangeValidation:
- {
- _fromDate = new DateTime(2009,1,1);
- _toDate = DateTime.Today;
- _defaultErrorMessage = message;
- break;
- }
- }
- }
- }
-
- protected override ValidationResult IsValid(object value, ValidationContext validationContext)
- {
- switch (_validationType)
- {
- case ValidationType.Compare:
- {
- var baseProperyInfo = validationContext.ObjectType.GetProperty(_propertyNameToCompare);
- var startDate = (DateTime)baseProperyInfo.GetValue(validationContext.ObjectInstance, null);
- if(value!=null)
- {
- DateTime enteredDate = (DateTime)value;
- if (thisDate <= startDate)
- {
- return new ValidationResult(_defaultErrorMessage);
- }
- }
- break;
- }
-
- case ValidationType.RangeValidation:
- {
- if(value!=null)
- {
- DateTime enteredDate = (DateTime)value;
- if (!(thisDate >= _fromDate && thisDate <= _toDate))
- {
- return new ValidationResult(_defaultErrorMessage);
- }
- }
- }
- }
- return null;
- }
The IsValid method above returns the ValidationResult that is a member of System.ComponentModel.DataAnnotation. Now with these pieces of code our DateValidationAttribute class is ready to be consumed. Actually this class may require a few changes for better performance. I am still looking for some changes, like the error message can be more specific and so on. But it is well enough to proceed.
Now, let's see how to use it as our Model class's property:
- [Display(Name = "From Date")]
- [Required]
- public DateTime FromDate { get; set; }
- [Display(Name = "To Date")]
- [Required]
- [DateValidation(ValidationType.Compare, "Selected date should be Less than From Date.", compareWith: "FromDate")
- public DateTime ToDate { get; set; }
Now getting to my Controller where I have created an Action method that provides a custom action on validation failure and success.
- public ActionResult DateValidate(AccountModel model)
- {
- if (!ModelState.IsValid)
- return View(model);
- return RedirectToAction("About");
- }
Conclusion
Winding up all your business validation logic into one custom validator will help you to better organize your code and usage. I hope this helps.