Introduction
Validating user input has always been a challenging task for web developers. Not only do we want validation logic executing in the browser, but also we must validate the logic running on the server. The client side logic gives users instant feedback on the information they entered into a web page and is an expected feature in today’s applications. Meanwhile the server validation logic is in place because you should never trust information arriving from the network.

We will see how we can implement our custom logic in data annotation attributes. If you are new in data annotation validation please read my first article for built in data annotation validation.
In this article we will try to implement our custom validation logic for address field in which address cannot accept more than 50 words.
Overview
For this article we create an application ASP.NET MVC application name DataAnnotationsValidations (you can download the source code for better understanding) and we are using Student Model Class that contains student relation information in which we are going to validate using Data Annotation.
- public class StudentModel
- {
- public Guid StudentId { get; set; }
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public DateTime DateOfBirth { get; set; }
- public string Address { get; set; }
- public string ContactNo { get; set; }
- public string EmailId { get; set; }
- public string ConfirmEmail { get; set; }
- public string UserName { get; set; }
- public string Password { get; set; }
- }
Student Controller
- using System.Web.Mvc;
- using DataAnnotationsValidations.Models;
- namespace DataAnnotationsValidations.Controllers
- {
- public class StudentController: Controller
- {
- // GET: Student
- public ActionResult Index()
- {
- return View();
- }
- // GET: Student/Create
- public ActionResult Create()
- {
- return View();
- }
- // POST: Student/Create
- [HttpPost]
- public ActionResult Create(StudentModel student)
- {
- try
- {
- if (ModelState.IsValid)
- {
- return RedirectToAction("Index");
- }
- return View();
- }
- catch
- {
- return View();
- }
- }
- }
- }

Create Student View
- @model DataAnnotationsValidations.Models.StudentModel
- @{
- ViewBag.Title = "Add Student";
- }
- <h3>Add New Student</h3>
- @using (Html.BeginForm())
- {
- @Html.AntiForgeryToken()
- <div class="form-horizontal">
- <hr />
- @Html.ValidationSummary(true, "", new { @class = "text-danger" })
- <div class="form-group">
- @Html.LabelFor(model => model.StudentId, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.StudentId, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.StudentId, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.DateOfBirth, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.DateOfBirth, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.DateOfBirth, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.Address, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Address, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Address, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.ContactNo, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.ContactNo, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.ContactNo, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.EmailId, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.EmailId, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.EmailId, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.ConfirmEmail, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.ConfirmEmail, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.ConfirmEmail, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.UserName, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.UserName, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- @Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })
- <div class="col-md-10">
- @Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
- @Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
- </div>
- </div>
- <div class="form-group">
- <div class="col-md-offset-2 col-md-10">
- <input type="submit" value="Create" class="btn btn-danger" />
- </div>
- </div>
- </div>
- }
Now we are going to discuss the validation available in data annotation one by one.
Note: Data annotations are attributes we can find in the System.ComponentModel.DataAnnotations namespace. These attributes provide server side validation and framework also supports client side validation.
Custom Annotations
Imagine we want to restrict the address field value of a student to a limited number of words. For example we might say 50 words is more than enough for address field. You might also think that this type of validation (limiting a string to a maximum number of words) is something we can reuse with other modules in our application. If so, the validation logic is a candidate for packaging into reusable attributes.
So making the attributes reusable we are going to place all the custom annotation logic in one common place/folder, say attributes as we know all the validation annotations (such as Range, StringFormat ) ultimately derive from the ValidationAttribute base class. This base class is abstract and resides under System.ComponentModel.DataAnnotations namespace.
To keep validation logic in one place I have added an attributes folder name in application solution and added a class MaxWordAttributes class in it like this
- using System.ComponentModel.DataAnnotations;
- namespace DataAnnotationsValidations.Attributes
- {
- public class MaxWordAttributes: ValidationAttribute
- {}
- }
- public class MaxWordAttributes: ValidationAttribute
- {
- protected override ValidationResult IsValid(object value, ValidationContext validationContext)
- {
- return ValidationResult.Success;
- }
- }
- public class MaxWordAttributes: ValidationAttribute
- {
- private readonly int _maxWords;
- public MaxWordAttributes(int maxWords)
- {
- _maxWords = maxWords;
- }
- protected override ValidationResult IsValid(object value, ValidationContext validationContext)
- {
- return ValidationResult.Success;
- }
- }
- public class MaxWordAttributes: ValidationAttribute
- {
- private readonly int _maxWords;
- public MaxWordAttributes(int maxWords)
- {
- _maxWords = maxWords;
- }
- protected override ValidationResult IsValid(object value, ValidationContext validationContext)
- {
- if (value == null) return ValidationResult.Success;
- var textValue = value.ToString();
- return textValue.Split(' ').Length > _maxWords ?
- new ValidationResult("Too long Address!") :
- ValidationResult.Success;
- }
- }
Here the problem with the last line new ValidationResult("Too long Address!") we can see this is just a hardcoded error message. To write a good quality code we never want hardcoded error message. We always want to pass the error message and that error message should display instead of this hardcoded one.
To do that we just need to change our code a bit.
- public class MaxWordAttributes: ValidationAttribute
- {
- private readonly int _maxWords;
- public MaxWordAttributes(int maxWords): base("{0} has to many words.")
- {
- _maxWords = maxWords;
- }
- protected override ValidationResult IsValid(object value, ValidationContext validationContext)
- {
- if (value == null) return ValidationResult.Success;
- var textValue = value.ToString();
- if (textValue.Split(' ').Length <= _maxWords) return ValidationResult.Success;
- var errorMessage = FormatErrorMessage((validationContext.DisplayName));
- return new ValidationResult(errorMessage);
- }
- }
- First we place the default error message to the base constructor. We should pull default error message from a resource file if we build an international stander application.
- Second, notice how the default error message includes a parameter placeholder ({0}). The placeholder exists because the second change, the call inherited FormatErrorMessage method, will automatically format the string using the display name property.
FormatErrorMessage ensures that we use correct error message string even if the string is localized into a resource file. We can use our custom annotation attributes in the following ways.
- [DataType(DataType.MultilineText)]
- [MaxWordAttributes(50)]
- public string Address { get; set; }
- [DataType(DataType.MultilineText)]
- [MaxWordAttributes(50,ErrorMessage="There are too many words in {0}.")]
- public string Address { get; set; }

Remote
The ASP.NET MVC frameworkadds an additional Remote validation attribute. This attributes is in System.Web.Mvc namespace.
The Remote attributes enable us to perform client side validation with server callback. Take for example the UserName property is of a StudentModel, we are not going to allow the user name that already exists in our database. Which is to say for every student there must be a unique UserName. To achieve this we need to use Remote attributes.
- [Remote("CheckUserName","Student")]
- public string UserName { get; set; }
- public JsonResult CheckUserName(string userName)
- {
- var studentUserList = new List < string >
- {
- "Manish",
- "Saurabh",
- "Akansha",
- "Ekta",
- "Rakesh",
- "Bhayia Jee"
- };
- var result = studentUserList.Any(userName.Contains);
- return Json(result, JsonRequestBehavior.AllowGet);
- }
Conclusion
In this article, we learned about the custom data annotation validation in ASP.NET MVC framework. If you have any question or comments regarding this article, please post it in the comment section of this article. I hope you like it.

margam chakriPosted Sep 4, 2017, 12:59 PM
How to use data annotation in wcf service
Asad AliPosted Jun 9, 2016, 8:57 AM
good one
Sonu ChaudharyPosted Jun 6, 2016, 9:45 AM
good one...
Vignesh ManiPosted Jun 6, 2016, 5:32 AM
Nice
Debasis SahaPosted Jun 6, 2016, 12:20 AM
Good One..
Kirtan ParmarPosted Jun 4, 2016, 7:20 AM
Interesting...