Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Beginner's Tutorial for Understanding and Implementing Custom Validations in ASP.NET MVC using DataAnnotations

0.00/5 (No votes)
6 Apr 2014 1  
In this article we will see how we can perform custom validations by using custom data annotations in ASP.NET MVC

Introduction

In this article we will see how we can perform custom validations by using custom data annotations in ASP.NET MVC i.e. implementing custom validation attributes in ASP.NET MVC.

Background

In one of my previous articles, I have talked about how we to use DataAnnotations to perform validations of Model classes in an ASP.NET MVC application. A Beginner's Tutorial on Validating Model Data and Unobtrusive Client side Validation in ASP.NET MVC[^].

In the same article we saw how we can use simple script includes and get out of the box support for unobtrusive client side validations too.

We have seen that using the data annotation technique we can perform following validations:

  • Required fields: RequiredAttribute
  • Restricting the length of user input: StringLengthAttribute
  • Regular expression based input constraints: RegularExpressionAttribute

Now what if we have requirements to perform custom validations. Would it be possible to perform custom validations using this DataAnnotation technique. In this article, we will be discussing how we can perform custom validations for Model properties by using data annotations i.e. implementing custom validation attributes.

Note: The above mentioned article shows how to perform these validations, please refer that article to know more about these validations. In this article, we will only be talking about custom validations.

Using the code

Now to understand the custom validations using data annotations, I will be continuing with the sample application from the previous article. so Let us take a quick look at what is there in the existing application.

Database

In this application we have a single table database. This table (called as Contacts) contain the information of a contact person.

Now in this table, All the fields are not-null i.e. they will be required from the user.

  • FirstName: varchar(50)
  • LastName: varchar(50)
  • Address: varchar(100)
  • PhoneNumber: varchar(15)
  • eMail: varchar(35)
  • Age: INT

Note: The age field has been added for this article to demonstrate a custom validation rule.

Models, Views and Controllers

To perform data access we are using entity framework database first approach. So our contact entity looks like:

The application contains views to perform CRUD operation on the model. and the controller for the performing these operations looks like:

public class ContactController : Controller
{
    private SampleDbEntities db = new SampleDbEntities();

    public ViewResult Index()
    {
        return View(db.Contacts.ToList());
    }

    public ViewResult Details(int id)
    {
        Contact contact = db.Contacts.Single(c => c.ID == id);
        return View(contact);
    }

    public ActionResult Create()
    {
        return View();
    } 

    [HttpPost]
    public ActionResult Create(Contact contact)
    {
        if (ModelState.IsValid)
        {
            db.Contacts.AddObject(contact);
            db.SaveChanges();
            return RedirectToAction("Index");  
        }

        return View(contact);
    }
         
    public ActionResult Edit(int id)
    {
        Contact contact = db.Contacts.Single(c => c.ID == id);
        return View(contact);
    }

    [HttpPost]
    public ActionResult Edit(Contact contact)
    {
        if (ModelState.IsValid)
        {
            db.Contacts.Attach(contact);
            db.ObjectStateManager.ChangeObjectState(contact, EntityState.Modified);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(contact);
    }
 
    public ActionResult Delete(int id)
    {
        Contact contact = db.Contacts.Single(c => c.ID == id);
        return View(contact);
    }

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {            
        Contact contact = db.Contacts.Single(c => c.ID == id);
        db.Contacts.DeleteObject(contact);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}

Existing validation Rules

To implement validations using DataAnnotation attributes in our Model class, we creates a partial class and added the data annotations on it.

[MetadataType(typeof(ContactMetaData))]
public partial class Contact
{
}

the partial class is adorned with the MetadataType attribute which is present in DataAnnotations namespace. This indicated that the meta data for this Model class will be present in ContactMetaData class. So this ContactMetaData class will be the place to put in all our validation logic. This meta data class contains same public properties as that of the Model class it is associated with.

Following validation rules have already been implemented in the previous article

  • All the fields are required.
  • The length of user input should not exceed the length of respective fields.
  • PhoneNumber should contain only numbers.
  • eMail should be in the proper email format.

Implementing Custom Validations

Now in this article, we will try to implement following CUSTOM validations in the same application:

  • Minimum age for entering any contact should be 18 years.
  • Phone numbers for the contact should be unique. i.e. user should not be able to enter same phone number for multiple contacts.

Now let us see how we can use data annotation technique to implement these custom validations. For this we need to create custom validation attributes.

To create custom validation attributes we need to create an Attribute class derived from ValidationAttribute. Let us try to implement the first custom validation rule i.e. "Minimum age for entering any contact should be 18 years". For this let us create a custom attribute class called as MinimumvalueAttribute.

public class MinimumvalueAttribute : ValidationAttribute
{
}

We need to override the IsValid function in this class so as to perform our custom validation. Let us take the specified minimum value in the constructor of the attribute and then we will perform the actual validation in the IsValid function.

public class MinimumvalueAttribute : ValidationAttribute
{
    private int m_minimumValue;

    public MinimumvalueAttribute(int value)
    {
        m_minimumValue = value;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value != null) // lets check if we have some value
        {
            if (value is int) // check if it is a valid integer
            {
                int suppliedValue = (int)value;
                if (suppliedValue < m_minimumValue)
                {
                    // let the user know about the validation error
                    return new ValidationResult("Minimum value for this field should be " + m_minimumValue);
                }
            }
        }

        return ValidationResult.Success;
    }
}

Now we have the custom attribute ready with our custom validation logic. Now let us use this attribute with in the model to associate our custom validation logic with the model property.

class ContactMetaData
{
    /*
    .
    . Other model properties
    .
    */

    [Required(ErrorMessage="Age is Required")]
    [Minimumvalue(18)] // Our custom attribute
    public int Age { get; set; }
}

Now if I run the application and enter a value less than 18, I will get a validation error.

Now on the same lines let us implement our second custom validation rule i.e. "Phone numbers for the contact should be unique". To implement this we will again define our custom attribute and in the IsValid function of the attribute we will check whether the phone number already exists in the database or not.

public class UniquePhoneNumberAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        string number = value as string;
        Contact contact = validationContext.ObjectInstance as Contact;

        if (number != null) 
        {
            using (SampleDbEntities db = new SampleDbEntities())
            {
                var contactFound = db.Contacts.FirstOrDefault(item => item.PhoneNumber.Trim().Equals(number.Trim(), StringComparison.OrdinalIgnoreCase));
                if (contactFound != null && contactFound.ID != contact.ID)
                {
                    return new ValidationResult("Same phone number exists in the application.");
                }
            }
        }

        return ValidationResult.Success;
    }
}

Now let us configure the ContactMetaData class to use this custom attribute for validating the phone number.

class ContactMetaData
{
    /*
    .
    . Other model properties
    .
    */

    [Required(ErrorMessage = "Phone Number is required")]
    [StringLength(15, ErrorMessage = "Phone Number length Should be less than 15")]
    [RegularExpression(@"^[0-9]{0,15}$", ErrorMessage = "PhoneNumber should contain only numbers")]
    [UniquePhoneNumber]  // OUR CUSTOM ATTRIBUTE
    public string PhoneNumber { get; set; }
}

Here we are checking whether the same phone number exist for any other user. If yes then we are showing the user a validation error message. Lets run the application and see the results.

Now we have an application that uses Data annotations to perform custom validations.

Point of interest

In this article we have looked at performing custom data annotation based validation by implementing custom validation attributes. This article has been written from the beginner's perspective. I hope this has been informative.

History

  • 04 April 2014: First version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here