asp.net-mvc

Data 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

Range Attribute

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

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= "admin@dotnetfiddle.net";

    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.


This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow