asp.net-mvcData annotations

Introduction

We can add validations to our application by adding Data Annotations to our model classes. Data Annotations allow us to describe the rules we want applied to our model properties, and ASP.NET MVC will take care of enforcing them and displaying appropriate messages to users.

Basic validation attributes used in ViewModel

Model

using System.ComponentModel.DataAnnotations;

public class ViewModel
{
    [Required(ErrorMessage="Name is required")] 
    public string Name { get; set; }

    [StringLength(14, MinimumLength = 14, ErrorMessage = "Invalid Phone Number")]
    [Required(ErrorMessage="Phone Number is required")] 
    public string PhoneNo { get; set; }

    [Range(typeof(decimal), "0", "150")]
    public decimal? Age { get; set; }

    [RegularExpression(@"^\d{5}(-\d{4})?$", ErrorMessage = "Invalid Zip Code.")]
    public string ZipCode {get;set;}

    [EmailAddress(ErrorMessage = "Invalid Email Address")] 
    public string Email { get; set; }

    [Editable(false)] 
    public string Address{ get; set; }
}

View

// Include Jquery and Unobstructive Js here for client side validation

@using (Html.BeginForm("Index","Home") { 

    @Html.TextBoxFor(model => model.Name) 
    @Html.ValidationMessageFor(model => model.Name)

    @Html.TextBoxFor(model => model.PhoneNo)
    @Html.ValidationMessageFor(model => model.PhoneNo)

    @Html.TextBoxFor(model => model.Age)
    @Html.ValidationMessageFor(model => model.Age)

    @Html.TextBoxFor(model => model.ZipCode)
    @Html.ValidationMessageFor(model => model.ZipCode)

    @Html.TextBoxFor(model => model.Email)
    @Html.ValidationMessageFor(model => model.Email)

    @Html.TextBoxFor(model => model.Address)
    @Html.ValidationMessageFor(model => model.Address)

    <input type="submit" value="submit" />
}

Controller

public ActionResult Index(ViewModel _Model) 
{ 
    // Checking whether the Form posted is valid one. 
    if(ModelState.IsValid) 
    { 
        // your model is valid here.
        // perform any actions you need to, like database actions,
        // and/or redirecting to other controllers and actions.
    }
    else 
    {
        // redirect to same action
        return View(_Model);
    } 
}

Remote validation

Remote Validation used to check whether the content enter in the input control is valid or not by sending an ajax request to server side to check it.

Working

The RemoteAttribute works by making an AJAX call from the client to a controller action with the value of the field being validated. The controller action then returns a JsonResult response indicating validation success or failure. Returning true from your action indicates that validation passed. Any other value indicates failure. If you return false, the error message specified in the attribute is used. If you return anything else such as a string or even an integer, it will be displayed as the error message. Unless you need your error message to be dynamic, it makes sense to return true or false and let the validator use the error message specified on the attribute.

ViewModel

public class ViewModel
{
    [Remote("IsEmailAvailable", "Group", HttpMethod = "POST", ErrorMessage = "Email already exists. Please enter a different email address.")]
    public string Email{ get; set; }
}

Controller

[HttpPost]
public JsonResult IsEmailAvailable(string Email)
{
    // Logic to check whether email is already registered or Not.
    var emailExists = IsEmailRegistered();
    return Json(!emailExists);         
} 

Live Demo Fiddle

You can pass additional properties of the model to the controller method using the AdditionalFields property of RemoteAttribute. A typical scenario would be to pass the ID property of the model in an 'Edit' form, so that the controller logic can ignore values for the existing record.

Model

  public int? ID { get; set; }
  [Display(Name = "Email address")]
  [DataType(DataType.EmailAddress)]
  [Required(ErrorMessage = "Please enter you email address")]
  [Remote("IsEmailAvailable", HttpMethod="Post", AdditionalFields="ID", ErrorMessage = "Email already exists. Please enter a different email address.")]  
  public string Email { get; set; }

Controller

[HttpPost]
public ActionResult Validate(string email, int? id)
{
    if (id.HasValue)
    {
        return Json(!db.Users.Any(x => x.Email == email && x.ID != id);
    }
    else
    {
        return Json(!db.Users.Any(x => x.Email == email);
    }
}

Working Demo - Additional Fields

Additional Note

The default error message is understandably vague, so always remember to override the default error message when using the RemoteAttribute.

RequiredAttribute

The Required attribute specifies that a property is required. An error message can be specified on using the ErrorMessage property on the attribute.

First add the namespace:

using System.ComponentModel.DataAnnotations;

And apply the attribute on a property.

public class Product
{
   [Required(ErrorMessage = "The product name is required.")]
   public string Name { get; set; }

   [Required(ErrorMessage = "The product description is required.")]
   public string Description { get; set; }
}

It is also possible to use resources in the error message for globalized applications. In this case, the ErrorMessageResourceName must be specified with the resource key of the resource class (resx file) that must be setted on the ErrorMessageResourceType:

public class Product
{
   [Required(ErrorMessageResourceName = "ProductNameRequired", 
             ErrorMessageResourceType = typeof(ResourceClass))]
   public string Name { get; set; }

   [Required(ErrorMessageResourceName = "ProductDescriptionRequired", 
             ErrorMessageResourceType = typeof(ResourceClass))]
   public string Description { get; set; }
}

StringLengthAttribute

The StringLength attribute specifies the minimum and maximum length of characters that are allowed in a data field. This attribute can be applied on properties, public fields and parameters. The error message must be specified on the ErrorMessage property on the attribute. The properties MinimumLength and MaximumLength specifies the minimum and maximum respectively.

First add the namespace:

using System.ComponentModel.DataAnnotations;

And apply the attribute on a property.

public class User
{
   // set the maximum
   [StringLength(20, ErrorMessage = "The username cannot exceed 20 characters. ")]
   public string Username { get; set; }

   [StringLength(MinimumLength = 3, MaximumLength = 16, ErrorMessage = "The password must have between 3 and 16 characters.")]        
   public string Password { get; set; }
}

It is also possible to use resources in the error message for globalized applications. In this case, the ErrorMessageResourceName must be specified with the resource key of the resource class (resx file) that must be setted on the ErrorMessageResourceType:

public class User
{
   [StringLength(20, ErrorMessageResourceName = "StringLength", 
                              ErrorMessageResourceType = typeof(ResoucesKeys))]        
   public string Username { get; set; }

   [StringLength(MinimumLength = 3, 
                        MaximumLength = 16, 
                        ErrorMessageResourceName = "StringLength", 
                        ErrorMessageResourceType = typeof(ResoucesKeys))]        
   public string Password { get; set; }
}

Range Attribute

The Range attribute can decorate any properties or public fields and specifies a range that a numerical field must fall between to be considered valid.

[Range(minimumValue, maximumValue)]
public int Property { get; set; }

Additionally, it accepts an optional ErrorMessage property that can be used to set the message received by the user when invalid data is entered :

[Range(minimumValue, maximumValue, ErrorMessage = "{your-error-message}")]
public int Property { get; set; }

Example

[Range(1,100, ErrorMessage = "Ranking must be between 1 and 100.")]
public int Ranking { get; set; }

RegularExpression Attribute

The [RegularExpression] attribute can decorate any properties or public fields and specifies a regular expression that must be matched for the property be considered valid.

[RegularExpression(validationExpression)]
public string Property { get; set; }

Additionally, it accepts an optional ErrorMessage property that can be used to set the message received by the user when invalid data is entered :

[RegularExpression(validationExpression, ErrorMessage = "{your-error-message}")]
public string Property { get; set; }

Example(s)

[RegularExpression(@"^[a-z]{8,16}?$", ErrorMessage = "A User Name must consist of 8-16 lowercase letters")]
public string UserName{ get; set; }
[RegularExpression(@"^\d{5}(-\d{4})?$", ErrorMessage = "Please enter a valid ZIP Code (e.g. 12345, 12345-1234)")]
public string ZipCode { get; set; }

Compare Attribute

The Compare attribute compares two properties of a model.

The error message can be specified using property ErrorMessage, or using resource files.

To use Compare attribute include using for the following namespace:

using System.ComponentModel.DataAnnotations;

Then you can use the attribute in your model:

public class RegisterModel
{
    public string Email { get; set; }

    [Compare("Email",  ErrorMessage = "The Email and Confirm Email fields do not match.")]
    public string ConfirmEmail { get; set; }
}

When this model is validates, if Email and ConfirmEmail have different values, validation will fail.

Localized error messages

Just like with all validation attributes, it is possible to use error messages from resource files. In this sample the error message will be loaded from resource file Resources, resource name is CompareValidationMessage:

public class RegisterModel
{
    public string Email { get; set; }

    ["Email", ErrorMessageResourceType = typeof(Resources),  ErrorMessageResourceName = "CompareValidationMessage")]
    public string ConfirmEmail { get; set; }
}

Avoid strings in property names

To avoid using string for property value, in C# 6+ you can use nameof keyword:

public class RegisterModel
{
    public string Email { get; set; }

    [Compare(nameof(Email),  ErrorMessage = "The Email and Confirm Email fields do not match.")]
    public string ConfirmEmail { get; set; }
}

Placeholders in error messages

You can use placeholders in your error messages. Placeholder {0} is replaced with the display name of current property and {1} is replaced with display name of related property:

public class RegisterModel
{
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Display(Name = "Confirm Email")]
    [Compare("Email",  ErrorMessage = "The '{1}' and '{0}' fields do not match.")]
    public string ConfirmEmail { get; set; }
}

If validation of the model fails, the error message will be

The 'Email' and 'Confirm Email' fields do not match.

Custom Validation Attribute

When it comes to validate some rules which are not generic data validation e.g ensuring a field is required or some range of values but they are specific to your business logic then you can create your own Custom Validator. To create a custom validation attribute, you just need to inherit ValidationAttribute class and override its IsValid method. The IsValid method takes two parameters, the first is an object named as value and the second is a ValidationContext object named as validationContext. Value refers to the actual value from the field that your custom validator is going to validate.

Suppose you want to validate Email through Custom Validator

public class MyCustomValidator : ValidationAttribute
{
    private static string myEmail= "[email protected]";

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        string Email = value.ToString();
        if(myEmail.Equals(Email))
            return new ValidationResult("Email Already Exist");
        return ValidationResult.Success;
    }
}

public class SampleViewModel
{
    [MyCustomValidator]
    [Required]
    public string Email { get; set; }

    public string Name { get; set; }
}

Here is its DotNetFiddle Demo

EDMx model - Data Annotation

Edmx model internel

public partial class ItemRequest
{
    public int RequestId { get; set; }
    //...
}

Adding data annotation to this - if we modify this model directly, when a update to the model is made, the changes are lost . so

To add a attribute in this case 'Required'

Create a new class - any name Then

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

//make sure the namespace is equal to the other partial class ItemRequest
namespace MvcApplication1.Models 
{
    [MetadataType(typeof(ItemRequestMetaData))]
    public partial class ItemRequest
    {
    }

    public class ItemRequestMetaData
    {
        [Required]
        public int RequestId {get;set;}

        //...
    }
}

or

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace YourApplication.Models
{
    public interface IEntityMetadata
    {
        [Required]
        Int32 Id { get; set; }
    }

    [MetadataType(typeof(IEntityMetadata))]
    public partial class Entity : IEntityMetadata
    {
        /* Id property has already existed in the mapped class */
    }
}

Data annotations for Database first implementation (model code auto-generated)

[MetadataType(typeof(RoleMetaData))]
public partial class ROLE
{
}

public class RoleMetaData
{
    [Display(Name = "Role")]
    public string ROLE_DESCRIPTION { get; set; }

    [Display(Name = "Username")]
    public string ROLE_USERNAME { get; set; }
}

If you used database-first and your model code was auto-generated, this message will appear above your model code:

This code was generated from a template. Manual changes to this file may cause unexpected behavior in your application. Manual changes to this file will be overwritten if the code is regenerated

If you want to use data-annotations and you don't want them to be over-written if you refresh the edmx just add another a partial class to your model folder that looks like the example above.