by Stian

ASP.NET MVC with Cleaner Data Annotations


While using data annotations with localization in your MVC model, you may end up with some untidy looking code like in this example, where all we do is adding display information and validation:

   1:  public class Character {
   2:    [Display(Name="Character_FirstName", ResourceType=typeof(ClassLib1.Resources))]
   3:    [Required(ErrorMessageResourceType=typeof(ClassLib1.Resources), 
   4:      ErrorMessageResourceName="Character_FirstName_Required")]
   5:    [StringLength(50, ErrorMessageResourceType = typeof(ClassLib1.Resources),
   6:      ErrorMessageResourceName = "Character_FirstName_StringLength")]
   7:    public string FirstName { get; set; }
   9:    [Display(Name="Character_LastName", ResourceType=typeof(ClassLib1.Resources))]
  10:    [Required(ErrorMessageResourceType=typeof(ClassLib1.Resources), 
  11:      ErrorMessageResourceName="Character_LastName_Required")]
  12:    [StringLength(50, ErrorMessageResourceType = typeof(ClassLib1.Resources),
  13:      ErrorMessageResourceName = "Character_LastName_StringLength")]
  14:    public string LastName { get; set; }
  15:  }


While working on a project I found a NuGet Package which helped me to clean up this “mess”.


The extension is created by Phil Haack and derives from the DataAnnotationsModelMetadataProvider. The beauty of this extension is that you can set up a default resource type in Global.asax like this:

   1:  ModelMetadataProviders.Current = new ConventionalModelMetadataProvider(
   2:    requireConventionAttribute: false,
   3:    defaultResourceType: typeof(MyResources.Resource)
   4:  );

Note that the first argument, requireConventionAttribute, determines whether the conventions only apply to classes with the MetadataConventionsAttribute applied.

After you have configured the default resource type, it is no longer necessary to specify the ResourceType or ErrorMessageResourceType attributes. Also, the custom metadata provider looks up the resource key named the same as your property so that you are no longer required to specifying Display(Name=”FirstName”) as long as you match the property name and the resource name.

The same goes for validation attributes where you can use a resource key of {PropertyName}_{Attributename}, for example, to locate the error message for a RequiredAttribute, the provider finds the resource key FirstName_Required.

The “messy” data annotation code from above could with this extension look something like the code below, and still give the same result as long as you have the resources “FirstName” and “LastName” in your default ResourceType:

   1:  public class Character {
   2:    [Required]
   3:    [StringLength(50)]
   4:    public string FirstName {get; set;}
   6:    [Required]
   7:    [StringLength(50)]
   8:    public string LastName {get; set;}
   9:  }

Hello good lookin’ !

You could of course provide some metadata, the metadata that you don’t supply is inferred based on the conventions. You may also like that if a value for a given resource is not found, the code falls back to using the property name as the label, but splits it using Pascal/Camel casing as a guide. Therefore in this case, the label is “First Name” and “Last Name”.

  • bme

    What if i have a field that contains html markup, how do I exclude the markup from [StringLength(300, Error)]
    300 length of text input + html code?

  • Stian

    I would recommend you use a javascript to clean the html and submit the input + html in another hidden field. This way you can validate length in original text input but still read the submitted markup. I don’t see how you could use annotations in any other way to accomplish this.. unless of course, you write a crazy kick ass regex expression 😀