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; }
}
// 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" />
}
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);
}
}
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);
}
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
.
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; }
}
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; }
}
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; }
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; }
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.
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 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 */
}
}
[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.