21

As far as I know the System.ComponentModel.DataAnnotations.DataTypeAttribute not works in model validation in MVC v1. For example,

public class Model
{
  [DataType("EmailAddress")]
  public string Email {get; set;}
}

In the codes above, the Email property will not be validated in MVC v1. Is it working in MVC v2?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
wenqiang
  • 954
  • 1
  • 5
  • 14

5 Answers5

35

[DataType("EmailAddress")] doesn't influence validation by default. This is IsValid method of this attribute (from reflector):

public override bool IsValid(object value)
{
    return true;
}

This is example of custom DataTypeAttribute to validate Emails (taken from this site http://davidhayden.com/blog/dave/archive/2009/08/12/CustomDataTypeAttributeValidationCustomDisplay.aspx):

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple  = false)]
public class EmailAddressAttribute : DataTypeAttribute
{
    private readonly Regex regex = new Regex(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", RegexOptions.Compiled);

    public EmailAddressAttribute() : base(DataType.EmailAddress)
    {

    }

    public override bool IsValid(object value)
    {

        string str = Convert.ToString(value, CultureInfo.CurrentCulture);
        if (string.IsNullOrEmpty(str))
            return true;

        Match match = regex.Match(str);   
        return ((match.Success && (match.Index == 0)) && (match.Length == str.Length));
    }
}
Artur Carvalho
  • 6,901
  • 10
  • 76
  • 105
LukLed
  • 31,452
  • 17
  • 82
  • 107
  • I think you want to make that regex static because setting the compiled option may cause compilation to be done everwhere the attribute is used and, if memory serves, generating a new in-memory assembly each time. – Mike Scott May 31 '10 at 17:45
  • @Mike Scott: New in-memory assembly? What do you mean by that? – LukLed May 31 '10 at 21:32
  • LukLed, what it says on the tin - an assembly created on the fly in memory ;-) Regex with the compiled option emits IL into an in-memory assembly. So to be sure you don't use up memory with each invocation, you should save the compiled regex instance and reuse it. see http://blogs.msdn.com/b/bclteam/archive/2004/11/12/256783.aspx. – Mike Scott Jun 02 '10 at 17:53
  • @Mike Scott: you're right, but if I'm not mistaken, an attribute is only instantiated once *per class*, not per object. This means that in practice the cost is pretty small. Nevertheless you're right that there's no cost in it being static. Should've been a const if the language would allow. – skrebbel Dec 25 '11 at 16:16
  • @skrebbel, so if you put the attribute on 10 classes, doesn't that mean 10 instantiations of the attribute and therefore 10 in-memory assemblies created for the same regex expression? – Mike Scott Feb 20 '12 at 14:28
  • Sure thing! But not 10 in-memory assemblies per class *instance*. Still a significant difference. But yeah, good point :) – skrebbel Feb 22 '12 at 13:14
  • @skrebbel, absolutely! But then I did say everywhere the attribute is used - you mentioned instances, not me ;-) If you put the attribute without the regex declared static on 10 classes, you'll create 10 in-memory assemblies instead of 1. The real point here is that over-ambitious use of compiled regexes can use a lot of memory and even cause memory leaks. You have to be careful to declare them statically in most cases. – Mike Scott Apr 01 '12 at 12:37
  • @LukLed -- The link is now broken. – KSwift87 Nov 12 '13 at 14:40
13

Like LukLed pointed out, DataTypeAttribute doesn't do any validation by default. But it does influence templates regarding how the data is presented.

For example if you call Html.DisplayFor() method on a model that has DataType(DataType.EmailAddress) attribute, it'll format its value with <a href="mailto:{0}">{0}</a> (at least in MVC RC2).

Community
  • 1
  • 1
Çağdaş Tekin
  • 16,592
  • 4
  • 49
  • 58
  • 2
    Thanks for point out that DataTypeAttribute is not a validation attribute. – wenqiang Mar 06 '10 at 06:17
  • 1
    @Wayne: It actually is ValidationAttribute. It inherits from ValidationAttribute, but always returns true. You can override IsValid method and define your own, as it is in David Hayden's solution. – LukLed Mar 06 '10 at 06:19
  • @LukLed, It indeed inherits from ValidationAttribute. I wonder why. Anyway, will correct that part. – Çağdaş Tekin Mar 06 '10 at 06:24
  • `DataAnnotationsModelValidator` goes through every ValidationAttribute. DataTypeAttribute is ValidationAttribute, so it could be easily inherited and validation introduced. When you define your own data type, you can easily add validation. – LukLed Mar 06 '10 at 06:31
  • @LukLed, Thanks. I edited the answer again to delete the part where it said it's not used for validation. – Çağdaş Tekin Mar 06 '10 at 06:45
9

Alternatively, you can directly use RegularExpression attribute on your field instead of creating your own attribute that is in the end going to check for a regex matching.

[RegularExpression(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", ErrorMessage = PaErrorMessages.InvalidEmailAddress)]
public string Email { get; set; }
Etienne
  • 1,058
  • 11
  • 22
2

Starting from .NET 4.5 there is EmailAddressAttribute, which has a correct implementation of the IsValid method. So if you're targeting .NET 4.5, then for validation please consider using the EmailAddressAttribute instead of a custom one. For example,

public class Model
{
    [EmailAddress(ErrorMessage = "INVALID EMAIL")]
    public string Email {get; set;}
}

And if you're curious about the implementation of EmailAddressAttribute, then here is the decompiled (using JetBrains dotPeek decompiler) source of the class:

using System;
using System.ComponentModel.DataAnnotations.Resources;
using System.Text.RegularExpressions;

namespace System.ComponentModel.DataAnnotations
{
  [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
  public sealed class EmailAddressAttribute : DataTypeAttribute
  {
    private static Regex _regex = new Regex("^((([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+(\\.([a-z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(\\\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|\\d|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.)+(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])|(([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])([a-z]|\\d|-|\\.|_|~|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])*([a-z]|[\\u00A0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF])))\\.?$", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled);

    static EmailAddressAttribute()
    {
    }

    public EmailAddressAttribute()
      : base(DataType.EmailAddress)
    {
      this.ErrorMessage = DataAnnotationsResources.EmailAddressAttribute_Invalid;
    }

    public override bool IsValid(object value)
    {
      if (value == null)
        return true;
      string input = value as string;
      if (input != null)
        return EmailAddressAttribute._regex.Match(input).Length > 0;
      else
        return false;
    }
  }
}
John Mills
  • 10,020
  • 12
  • 74
  • 121
ajukraine
  • 561
  • 9
  • 27
1

Check out Scott Guthrie's blog post on MVC 2 validation. It is excellent. http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx

Rick
  • 988
  • 2
  • 13
  • 23