19

We manage several ASP.NET MVC client web sites, which all use a data annotation like the following to validate customer email addresses (I haven't included the regex here, for readability):

[Required(ErrorMessage="Email is required")]
[RegularExpression(@"MYREGEX", ErrorMessage = "Email address is not valid")]
public string Email { get; set; }

What I would like to do is to centralise this regular expression, so that if we make a change to it, all of the sites immediately pick it up and we don't have to manually change it in each one.

The problem is that the regex argument of the data annotation must be a constant, so I cannot assign a value I've retrieved from a config file or database at runtime (which was my first thought).

Can anyone help me with a clever solution to this—or failing that, an alternative approach which will work to achieve the same goal? Or does this just require us to write a specialist custom validation attribute which will accept non-constant values?

Mark Bell
  • 28,985
  • 26
  • 118
  • 145
  • [Email Regular Expression MVC C#](http://lesson8.blogspot.com/2013/03/email-regular-expression-mvc-c.html) – Sender Oct 03 '13 at 16:57
  • 2
    `[EmailAddress(ErrorMessage="Not a valid Email Address")]` is already included in `System.ComponentModel.DataAnnotations.dll` – D.Rosado Dec 02 '13 at 16:27

4 Answers4

31

The easiest way is to write a custom ValidationAttribute that inherits from RegularExpressionAttribute, so something like:

public class EmailAttribute : RegularExpressionAttribute
    {
        public EmailAttribute()
            : base(GetRegex())
        { }

        private static string GetRegex()
        {
            // TODO: Go off and get your RegEx here
            return @"^[\w-]+(\.[\w-]+)*@([a-z0-9-]+(\.[a-z0-9-]+)*?\.[a-z]{2,6}|(\d{1,3}\.){3}\d{1,3})(:\d{4})?$";
        }
    }

That way, you still maintain use of the built in Regex validation but you can customise it. You'd just simply use it like:

[Email(ErrorMessage = "Please use a valid email address")]

Lastly, to get to client side validation to work, you would simply add the following in your Application_Start method within Global.asax, to tell MVC to use the normal regular expression validation for this validator:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(EmailAttribute), typeof(RegularExpressionAttributeAdapter));
Mark Bell
  • 28,985
  • 26
  • 118
  • 145
Ian Routledge
  • 4,012
  • 1
  • 23
  • 28
  • I'm accepting this because it was a simple, complete solution, explained well. Thanks! – Mark Bell Dec 08 '11 at 13:23
  • This is going back a while, but can someone explain just how they got their regex out of the resource file? I can get the expected languages in my my model (for the error message, for instance) but I always get the regular expression for the default `.resx` file from the extension when used above. Any ideas? – Alan Shortis Feb 17 '14 at 17:37
  • Hi Alan, See http://msdn.microsoft.com/en-us/library/ms227982(v=vs.100).aspx which explains how to grab resource values programatically - you could use `GetGlobalResourceObject` in the `GetRegex` method to access your localised regex. Alternatively if you make Resources public (see http://holyhoehle.wordpress.com/2010/02/20/making-global-resources-public/) then you could just access `Resource.Strings.SomeRegex` etc. – Ian Routledge Feb 17 '14 at 18:01
  • Thanks Ian. In my `GetRegex` method, I am passing in the value in my resx file as a parameter then returning the expression like this: `return Resources.FormValidation.ResourceManager.GetString(value);` It's mostly working, but I always get the value in my default resx file and never the one with a named culture. I have an open question here with more information (and maybe FIFTY points for you!): http://stackoverflow.com/questions/21735918/data-annotations-using-an-attribute-extension-and-storing-regular-expressions – Alan Shortis Feb 19 '14 at 14:19
3

Checkout ScotGu's [Email] attribute (Step 4: Creating a Custom [Email] Validation Attribute).

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
3

Do you really want to put the regex in database/config file, or do you just want to centralise them? If you just want to put the regex together, you can just define and use constants like

public class ValidationRegularExpressions {
    public const string Regex1 = "...";
    public const string Regex2 = "...";
}

Maybe you want to manage the regexes in external files, you can write a MSBuild task to do the replacement when you build for production.

If you REALLY want to change the validation regex at runtime, define your own ValidationAttribute, like

[RegexByKey("MyKey", ErrorMessage = "Email address is not valid")]
public string Email { get; set; }

It's just a piece of code to write:

public class RegexByKeyAttribute : ValidationAttribute {
    public RegexByKey(string key) {
        ...
    }

    // override some methods
    public override bool IsValid(object value) {
        ...
    }
}

Or even just:

public class RegexByKeyAttribute : RegularExpressionAttribute {
    public RegexByKey(string key) : base(LoadRegex(key)) { }

    // Be careful to cache the regex is this operation is expensive.
    private static string LoadRegex(string key) { ... }
}

Hope it's helpful: http://msdn.microsoft.com/en-us/library/cc668224.aspx

Mark Bell
  • 28,985
  • 26
  • 118
  • 145
Jeffrey Zhao
  • 4,923
  • 4
  • 30
  • 52
2

Why not just write you own ValidationAttribute?

http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.validationattribute.aspx

Then you can configure that thing to pull the regex from a registry setting... config file... database... etc... etc..

How to: Customize Data Field Validation in the Data Model Using Custom

John Sobolewski
  • 4,512
  • 1
  • 20
  • 26