ASP.NET MVC
Custom Validation
DataAnnotations
Web Development
C#

ASP.NET MVC Custom Validation by DataAnnotation

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

ASP.NET MVC supports validation through DataAnnotations, which lets you place validation rules directly on model properties. Built-in attributes cover many common cases, but custom validation becomes necessary when the rule is domain-specific. The usual way to do that is to create your own attribute by inheriting from ValidationAttribute.

Create a Custom Validation Attribute

A custom DataAnnotation starts by subclassing ValidationAttribute and overriding IsValid.

csharp
1using System;
2using System.ComponentModel.DataAnnotations;
3
4public class MinimumAgeAttribute : ValidationAttribute
5{
6    private readonly int _minimumAge;
7
8    public MinimumAgeAttribute(int minimumAge)
9    {
10        _minimumAge = minimumAge;
11    }
12
13    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
14    {
15        if (value == null)
16        {
17            return ValidationResult.Success;
18        }
19
20        if (value is DateTime birthDate)
21        {
22            var today = DateTime.Today;
23            var age = today.Year - birthDate.Year;
24
25            if (birthDate.Date > today.AddYears(-age))
26            {
27                age--;
28            }
29
30            if (age >= _minimumAge)
31            {
32                return ValidationResult.Success;
33            }
34        }
35
36        return new ValidationResult(ErrorMessage ?? $"Age must be at least {_minimumAge}.");
37    }
38}

This attribute checks whether the given birth date satisfies the minimum age rule.

Apply the Attribute to a Model

Once the attribute exists, use it like any built-in DataAnnotation:

csharp
1using System;
2using System.ComponentModel.DataAnnotations;
3
4public class RegistrationViewModel
5{
6    [Required]
7    public string Name { get; set; }
8
9    [MinimumAge(18, ErrorMessage = "You must be at least 18 years old.")]
10    public DateTime? BirthDate { get; set; }
11}

When model binding runs, MVC evaluates the attribute and adds validation errors to ModelState if the rule fails.

Check ModelState in the Controller

Custom attributes participate in the normal validation flow:

csharp
1[HttpPost]
2public ActionResult Register(RegistrationViewModel model)
3{
4    if (!ModelState.IsValid)
5    {
6        return View(model);
7    }
8
9    return RedirectToAction("Success");
10}

This is why custom DataAnnotations feel natural in MVC. They integrate with the same validation pipeline as Required, Range, and the other built-in attributes.

When Property-Level Validation Is Not Enough

Some rules depend on multiple properties rather than one field in isolation. For example, one date may need to be after another. In those cases, IValidatableObject is often a better fit than a property-level custom attribute.

csharp
1using System.Collections.Generic;
2using System.ComponentModel.DataAnnotations;
3
4public class BookingViewModel : IValidatableObject
5{
6    public DateTime StartDate { get; set; }
7    public DateTime EndDate { get; set; }
8
9    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
10    {
11        if (EndDate < StartDate)
12        {
13            yield return new ValidationResult(
14                "End date must be after start date.",
15                new[] { nameof(EndDate) }
16            );
17        }
18    }
19}

This is often cleaner than forcing cross-field logic into a single-property attribute.

Server Validation Versus Client Validation

A basic custom ValidationAttribute gives you server-side validation automatically. Client-side validation is a separate concern. In classic ASP.NET MVC, extra work is required if you want the browser to enforce the same custom rule through unobtrusive validation.

That does not make the custom attribute incomplete. It just means you should be clear about where the rule is being enforced.

Common Pitfalls

One common mistake is putting cross-property logic into a property-level attribute when IValidatableObject would express the rule more cleanly.

Another mistake is assuming that a custom ValidationAttribute automatically produces client-side validation behavior. Server-side validation comes first; client-side support may require additional wiring.

Developers also sometimes return false or throw exceptions instead of returning a proper ValidationResult. A clear validation result makes the MVC pipeline behave predictably.

Finally, remember that null handling is a design choice. If the field must be present, combine your custom attribute with Required instead of making the custom attribute responsible for every possible rule.

Summary

  • Custom MVC DataAnnotation validation starts by inheriting from ValidationAttribute.
  • Apply the custom attribute to model properties just like built-in validation attributes.
  • MVC adds validation failures to ModelState automatically during model binding.
  • Use IValidatableObject when the rule depends on multiple properties.
  • Server-side validation is automatic; client-side custom validation usually needs extra setup.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.