The original Published time: 2011-07-20--from my Baidu article [imported by moving tools]
Model Metadata and Validation Localization using conventions
Http://haacked.com/archive/2011/07/14/model-metadata-and-validation-localization-using-conventions.aspx
By default, ASP. NET MVC leverages Data Annotations to provide validation. The approach is easy-to-get started with and allows the validation applied on the server to ' float ' to the client without Any extra work.
However, once you get localization involved, using Data Annotations can really clutter your models. For example, the following are a simple model class with the properties.
Publicclass Character {publicstring FirstName {get; set;} publicstring LastName {get; set;}}
Nothing to write home on, but it's nice, clean, and simple. To do it more useful, I'll add validation and format how the properties is displayed.
Publicclass Character {[Display (name= "First Name")] [Required] [stringlength]] publicstring FirstName {get; set;} [ Display (name= "last Name")] [Required] [Stringlength ()]] publicstring LastName {get; set;}}
That's busier, but not horrible. It sure is awful anglo-centric though. I'll fix this by making sure the property labels and error messages is pulled from a resource file.
Publicclass Character {[Display (name= "Character_firstname", Resourcetype=typeof (Classlib1.resources))] [Required ( Errormessageresourcetype=typeof (classlib1.resources), errormessageresourcename= "Character_FirstName_Required")] [Stringlength (Errormessageresourcetype = typeof (Classlib1.resources), errormessageresourcename = "Character_ Firstname_stringlength ")] publicstring FirstName {get; set;} [Display (Name= "Character_lastname", Resourcetype=typeof (classlib1.resources))] [Required (Errormessageresourcetype=typeof (classlib1.resources), errormessageresourcename= "Character_LastName_ Required ")] [Stringlength (Errormessageresourcetype = typeof (Classlib1.resources), Errormessageresourcename =" Character_lastname_stringlength ")] publicstring LastName {get; set;}}
wow! I don ' t know about you, but I feel a little bit dirty typing all the. Allow me a moment as I go wash up.
So how can I do to get rid of any that noise? Conventions to the rescue! By employing a-simple set of conventions, I should is able to look up error messages in resource files as well as property Labels without has to specify all that information. In fact, by convention I shouldn ' t even need to use the DisplayAttribute.
I wrote a custom PROOF of CONCEPT Modelmetadataprovider that supports this approach. More specifically, mine are derived from the Dataannotationsmodelmetadataprovider.
What conventions Does It Apply?
The nice thing on this convention based model metadata provider are it allows you to specify as little or as much of the Metadata need and it fills in the rest.
Providing minimal metadata
For example, the following are a class with one.
Publicclass Character {[Required] [Stringlength ()] publicstring FirstName {get; set;}}
When displayed as a label, the custom metadata provider looks up the resource key, {classname}_{propertyname}, an D uses the resource value as the label. For example, the FirstName property, the provider uses the key character_firstname the E file. I ' ll cover how resource type is specified later.
If a value for this resource is not found, the code falls back to using the property name as the label, but splits it usin G Pascal/camel casing as a guide. The label is "firstName" in the Therefore.
The error message for a validation attribute uses a resource key of {classname}_{propertyname}_{attributename}. For example, to locate the error message for a RequiredAttribute, the provider finds the resource key character_firstname_ Required.
Partial Metadata
There may is cases where you can provide some metadata, and not all of it. Ideally, the metadata that you don ' t supply is inferred based on the conventions. Going back to previous example again:
Publicclass Character {[Required (errormessageresourcetype=typeof (MyResources))] [Stringlength (50, Errormessageresourcename= "Stringlength_error")] [Display (name= "First Name")] publicstring FirstName {get; set;}}
Notice The first attribute only specifies the error message resource type. In this case, the specified resource type would override the default resource type. But the resource key was still inferred by convention (aka Character_firstname_required).
In contrast, notice so the second Stringlengthattribute, only specifies the resource name, and doesn ' t specify a RESOURC E type. In this case, the specified resource name is used-to-look-up the error message using the default resource type. As you might expect, if the errormessage are specified, that's takes precedence over the conventions.
The DisplayAttribute works slightly differently. By default, the Name property is used as a resource key if a resource type is also specified. If No resource type is specified, the Name property is used directly. In the case of this Convention based provider, an attempt to lookup a resource value using the Name property as a resource Always occurs before falling back to the default behavior.
Configuration
One detail I haven ' t covered yet is what resource type was used to find these messages? Is this determined by convention?
Deteriming this by convention would is tricky so it's the one bit of information that must is explicitly specified when CO Nfiguring the provider itself. The following code in Global.asax.cs shows what to configure.
Modelmetadataproviders.current = new Conventionalmodelmetadataprovider (Requireconventionattribute:false, Defaultresourcetype:typeof (Myresources.resource));
The model metadata provider ' s constructor has a arguments used to configure it.
Some developers would want the conventions to apply to every model, while others would want to be explicit and has models O PT in to this behavior. The first argument, Requireconventionattribute, determines whether the conventions only apply to classes with the Metadata Conventionsattribute applied.
The explicit folks would want to set this value to true so, only classes with the Metadataconventionsattribute applied To them (or classes in a assembly where the attribute is applied to the assembly) would use these conventions.
The attribute can also is used to specify the resource type for resource strings.
The second property specifies the default resource type to use for resource strings. Note that this can is overridden by any attribute that specifies its own resource type.
Caveats, issues, potholes
This code is something I hacked together and there be a few issues to consider that I could not easily work around. First of all, the implementation have to mutate properties of attributes. In general, this is a good the thing to does because attributes tend to be global. If other code relies on the attributes has their original values, this could cause issues.
I think for the most ASP. NET MVC applications (in the fact most Web applications period) This is not being an issue.
Another issue is, the conventions don ' t work for implied validation. For example, if you had a property of a simple value type (such as int), the Dataannotationsvalidatorprovider supplies a Requiredvalidator to validate the value. Since this validator didn ' t come from a attribute, it won ' t use my convention based lookup for its error messages.
I thought about making this work, but it's the hooks I need to does this without a large amount of code don ' t appear to be the Re. I ' d have to write my own validator provider (as far as I can tell) or register my own validator adapters in place of the D Efault ones. I wasn ' t up to the task just yet.
Try it Out
Live Demo:To see a demo of it in action, check out the live Demo site.
NuGet Package:To try it in your application, install it using Nuget:install-package modelmetadataextensions
Source Code:The source code is up on BitBucket.
Change MVC3 default error message model validation validation