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:
- Validation attributes
- 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) {
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; }
[DisplayFormat(DataFormatString="{0:C}")]
public object WritingCost;
[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.
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: