Click here to Skip to main content
15,881,812 members
Articles / Programming Languages / C++

Validation attributes in Code First

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
22 Nov 2015CPOL7 min read 10.3K   1  
DataAnnotations are really helpful when creating a model in Entity Framework Code-First. They are a simple way of instructing EF how you want the model to be structured and how it should translate the model into a relational database.

DataAnnotations are really helpful when creating a model in Entity Framework Code-First. They are a simple way of instructing EF how you want the model to be structured and how it should translate the model into a relational database. You can also use validation rules to specify what values are allowed in your parameters. When using MVC and scaffolding these validation rules are also used when creating the user interface for you. You get a lot for free by simply adding the right DataAnnotations to your model. It’s really something worth looking into if you haven’t done it so far.

The attributes used in Code-First can be classified into two categories:

  1. Validation attributes
  2. Database schema related attributes

In this post I’ll look into the first category and will also provide with code examples for each of the attributes. If you think I’ve missed out on any good ones you’re more then welcome to write a comment. The second post regarding the database related attributes can be found here.

How to use attributes

All attributes are written just before the property/class/function they’re validating, with zero or more mandatory parameters followed by a list of optional named parameters. The attribute StringLength, for example, has one mandatory parameter: MaximumLength

[StringLength(10)]
public string MembershipCode { get; set; }

…but can also attach named parameters like MinimumLength and ErrorMessage. When using Visual Studio you’re provided with a list of all available named parameters.

[StringLength(20, MinimumLength = 5, ErrorMessage = "Use 5-20 characters")]
public string UserName { get; set; }

Other attributes like Required can be without parameters just fine.

[Required]
public string CountryCode { get; set; }

How MVC uses validation attributes to validate incoming variables

In the controller’s scaffolded methods marked with HttpPost you can see the ModelState object being used. The ModelState object contains info about all the incoming parameters and whether they validate according to the specified model’s validation attributes. In the example below incoming variables are matched towards the Customer object. If the Customer object specifies that CountryCode is Required but no CountryCode is listed among the incoming variables, the ModelState would signal a validation failure and the user would be redirected back to the page with an error message. This validation is done automatically for you and shows one example of how useful the attributes can be.

[HttpPost]
public ActionResult Edit(Customer customer)
{
    if (ModelState.IsValid) {
        // Save customer
        return RedirectToAction("Index");
    } else {
        return View();
    } 
}

Validation attributes

Here follows a list of the most common validation attributes available when building a model in code-first.

Compare

Compare value of a property to another property. The Compare attribute exists in both System.Web.Mvc and System.Component.DataAnnotations so you need to specify namespace for attribute if both namespaces are included in the using section. The example below makes sure that the properties Email and EmailVerify are the same.

public class RegisterEmailAddressViewModel
{
    [Required]
    public int UserId { get; set; }

    [DataType(DataType.EmailAddress)]
    public string Email { get; set; }

    [System.ComponentModel.DataAnnotations.Compare("Email", 
        ErrorMessage = "Verify email is invalid")]
    public string EmailVerify { get; set; }
}

Please note that the example above is a view model and is not intended for database storage. If you use the Compare attribute in a database model class you probably also want to attach a [NotMapped] attribute as well since you don’t want to store the email address twice in the database.

CreditCard

Verifies, using a regular expression, that value is a valid credit card number. However, that doesn’t mean the card number is valid to buy with.

public class Transaction
{
    public int TransactionId { get; set; }
    public decimal Amount { get; set; }

    [CreditCard]
    public string CreditCardNumber { get; set; }
}

CustomValidation

Allows you to write your own validation attribute. Simply create a class implementing the ValidationAttribute class. Add a public static function that accepts a value to be compared and return an instance of ValidationResult.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class OldEnoughValidationAttribute : ValidationAttribute
{
    public static ValidationResult IsOldEnough(int givenAge)
    {
        if (givenAge >= 20)
            return ValidationResult.Success;
        else
            return new ValidationResult("You're not old enough");
    }
}

You can then use the newly created attribute in the following way by specifying the attribute and the static function performing the validation:

public class Customer 
{
    [CustomValidation(typeof(OldEnoughValidationAttribute), "IsOldEnough")]
    public int Age { get; set; 
}

Another way of creating and using custom validation is, instead of creating the public static class above, to override the existing IsValid function in the ValidationAttribute class.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class OldEnoughValidationAttribute : ValidationAttribute
{
    public int LimitAge { get; set; }
    public OldEnoughValidationAttribute(int limitAge)
    {
        LimitAge = limitAge;
    }
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        int val = (int)value;

        if (val >= LimitAge)
            return ValidationResult.Success;
        else
            return new ValidationResult(ErrorMessageString);
    }
}

You can then just mark your properties with this new attribute and the validation will be done automatically.

public class Customer 
{
    [OldEnoughValidation(20, ErrorMessage = "You're not old enough")]
    public int Age { get; set; 
}

 

DataType

<script type="text/javascript"><!-- google_ad_client = "ca-pub-4810343104561487"; /* Ad in text */ google_ad_slot = "2990537009"; google_ad_width = 300; google_ad_height = 250; //--> </script>
<script type="text/javascript" src="//pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
Allows you to specify a datatype that is more specific than the database intrinsic types. This can be used to provide more accurate validation and appropriate display for user. Valid datatypes are:

  • CreditCard
  • Currency
  • Custom
  • Date – date value
  • DateTime – date and time value
  • Duration
  • EmailAddress
  • Html – specify that Html code is expected
  • ImageUrl – url to an image
  • MultilineText – uses textarea instead of text as input type in forms.
  • Password
  • PhoneNumber
  • PostalCode
  • Text
  • Time
  • Upload – a file upload data type
  • Url

Use the attribute in the following way:

public class Customer
{
    [DataType(DataType.EmailAddress, ErrorMessage = "Not a valid email address")]
    public string Email { get; set; }
}

Display

Helps you specify a text that can be used as label for the property’s value when editing in forms.

public class Customer
{
    [Display(Name = "Email is confirmed")]
    public bool EmailIsConfirmed { get; set; }
}

DisplayFormat

This attribute is used with EditorFor and DisplayFor to specify what format to expect.

public class Post
{
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy HH:mm}")]
    public DateTime PostedDate { get; set; }

    // Display as currency
    [DisplayFormat(DataFormatString="{0:C}")]
    public object WritingCost;

    // Store NULL as NULL but show as empty string
    [DisplayFormat(ConvertEmptyStringToNull = true, NullDisplayText = "")]
    public string ExtraDescription { get; set; }
}

EmailAddress

Verify that string is formatted as a valid email address.

public class Customer
{
    [EmailAddress(ErrorMessage="You have to enter a valid email address")]
    public string EmailAddress { get; set; }
}

EnumDataType

This attribute is used to map a stored value to a descriptive string by using an enum.

public class Line
{
    [EnumDataType(typeof(IntColor))]
    public object LineColor { get; set; }
}
public enum IntColor 
{
    Red = 1,
    Blue = 2,
    Green = 3,
    [Description("Slimy yellow")]
    Yellow = 4
}

However, now that EntityFramework has Enum support it’s better to use the enum type directly in the code, as shown here:

public class Line
{
    public IntColor LineColor { get; set; }
}

You can then use the EnumDropDownListFor method to display all the enum values. However, the Description attribute in the enum doesn’t show in this drop down…maybe a bug that might be fixed one day.

@Html.EnumDropDownListFor(m => m.LineColor)

FileExtensions

Set valid file attributes for a field containing a file name. In this example we only allow jpg and jpeg as picture files.

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
       
    [FileExtensions(Extensions="jpg,jpeg")]
    public string Avatar { get; set; }
}

When we try to save a png file we get the following error in the model validation.
File Extension Attribute Error
If the property is without the [Required] attribute (as in this example) an empty file name will be accepted also.

MaxLength

Maximum length of array or string

public class Customer
{
    [MaxLength(20, ErrorMessage = "Name can't be longer than 20 characters")]
    public string Name { get; set; }
}

 

MembershipPassword (System.Web.Security)
Validates property as password with given requirements.

public class Customer
{
    [Required]
    public string UserName { get; set; }

    [MembershipPassword(
        MinRequiredPasswordLength=8, 
        ErrorMessage="Your password needs to be at least 8 characters long", 
        MinRequiredNonAlphanumericCharacters=1, 
        MinNonAlphanumericCharactersError="At least one of the characters needs to be non-alphanumeric")]
    public string Password { get; set; }
}

MinLength

Minimum length of array or string

public class Customer
{
    [MinLength(5, ErrorMessage = "Name can't be shorter than 5 characters")]
    public string Name { get; set; }
}

Phone

Verifies that value is a phone number. But what phone numbers work with this attribute? Only US numbers? No, the definition is quite broad and all number with or without country prefix and local prefix should work. The regular expression used is ^(\+\s?)?((?<!\+.*)\(\+?\d+([\s\-\.]?\d+)?\)|\d+)([\s\-\.]?(\(\d+([\s\-\.]?\d+)?\)|\d+))*(\s?(x|ext\.?)\s?\d+)?$ according to .NET Framework 4.5.1 Reference Source.

public class CustomerPhone
{
    public int Id { get; set; }
    [Phone]
    public string PhoneNumber { get; set; }
}

Range

Specify a numeric minimum and maximum range for a property.

public class Customer
{
    [Range(0, 120, ErrorMessage = "Age has to be between {1} and {2}")]
    public int Age { get; set; }
}

RegularExpression

Validates property against regular expression.

public class Customer
{
    [RegularExpression(@"^[A-Z]{2}$", 
        ErrorMessage = "Country code can only be two alphabetic characters in CAPITALS")]
    public string CountryCode { get; set; }
}

Required

Property must contain a value and can’t be left empty.

public class Customer
{
    [Required(
        AllowEmptyStrings=false, 
        ErrorMessage="User name can't be empty")]
    public string UserName { get; set; }

    [Required]
    public string City { get; set; }
}

StringLength

Specifies maximum length of a string with option to specify minimum length and error message.

public class Customer
{
    [StringLength(20, 
        MinimumLength = 5, 
        ErrorMessage = "UserName has to be between 5 and 20 characters")]
    public string UserName { get; set; }
}

UIHint

In MVC there are 9 built-in display templates: Boolean, Decimal, EmailAddress, HiddenInput, Html, Object, String, Text, and Url. Most of the time these templates work just fine. But by using the UIHint attribute you can create and set your own custom display templates. If you have a field (or a class) you want to be rendered in a special way, you can create a template for it in MVC and then mark all fields that are to use this template with the UIHint attribute. A template for a property is simply a view file, with the same name as the UIHint attribute, placed inside the folder Home/Shared/DisplayTemplates (for displaying) or Home/Shared/EditorTemplates (for editing). These folders has to be created if the don’t exist already.

Let’s say you have an image url field in your model that is marked-up in the following way.

public class Customer
{
    public int Id { get; set; }

    [Url]
    [UIHint("AvatarUrl")]
    public string AvatarUrl { get; set; }
}

In our example we want to display the picture rather than the url when user is viewing Customer in display mode. We therefore create a file named AvatarUrl.cshtml and place it inside the DisplayTemplates folder. Inside the file we write the following code:

@model string
<img src="@Model" />

Whenever we later on in MVC use DisplayFor for a property marked with the [UIHint("AvatarUrl")] attribute the picture will be shown instead of the raw URL.

Url

Verifies that value is an URL.

public class WebLink
{
    public int Id { get; set; }
    [Url]
    public string Url{ get; set; }
    public string Description { get;set; }
}

More resources

If you want to read more about DataAnnotations I suggest the following links:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO BJ Focus Ltd
United Kingdom United Kingdom
Johan is the CEO of BJ Focus Ltd, a UK company specialised in the development of business structure platforms helping companies to structure flows of organisable data.

He writes articles on the BJ Lab's blog, especially when his customers keep asking the same thing. You might find a few of these articles here on CodeProject.

Currently his focus is on building MVC web apps on the Azure platform, but PHP and MySQL get their share of attention as well.

Johan has double masters, in business and in software development, and an MCTS in .Net Framework 4, web applications.

Comments and Discussions

 
-- There are no messages in this forum --