Reputation: 982
I'm working on a ASP.NET MVC4 web application, specifically in a very typical CRUD interface.
I have a model class such as this:
[MetadataType(typeof(USER_Metadata))]
public partial class USER
{
public class USER_Metadata
{
[Required(ErrorMessageResourceName = "MISSING_EMAIL", ErrorMessageResourceType = typeof(Resources.General.Usuario))]
[StringLength(70, MinimumLength = 3,
ErrorMessageResourceName = "EMAIL_LENGTH_ERROR", ErrorMessageResourceType = typeof(Resources.General.Usuario))]
[DataType(DataType.EmailAddress)]
public string EMAIL { get; set; }
/* other, similarly annotated members */
}
public string EMAIL { get; set; }
}
Which I think is a pretty standard definition of a model class to be used in MVC. I'm using that metadata to validate the user input from a ViewPage, and everything has worked fairly easy.
I'm now trying to validate this conditions on the server side (to massively load users from a file), and I have no idea of how to invoke those validations from outside the view or a controller.
I feel that this is something that probably a lot of people must have tried to do; can my googling be so weak? Is it impossible? Do I have no other choice than to do this in the controller code using ModelState?
I tried using Validator, which I thought was the most obvious-looking way:
var validationResults = new List<System.ComponentModel.DataAnnotations.ValidationResult>();
var isValid = Validator.TryValidateObject(
instance: user,
validationContext: new ValidationContext(user, serviceProvider: null, items: null),
validationResults: validationResults,
validateAllProperties: true);
But that always gives me true. I've also tried using reflection, but to no avail (my knowledge is too shallow in that area).
Upvotes: 2
Views: 1780
Reputation: 34992
I was curious about this behaviour so I have researched a bit. The attribute and metadata class will help on your scenario (model class code generated from the database), but take into account that USER
won't inherit the metadata in USER_Metadata
, there is no link between those classes other than the MetadataType
attribute. It's up to the validation code to interpret it!
Unfortunately it seems the Validator
class does not respects the [MetadataType]
attribute, so you will need to manually register yourself. I found it explained in this SO question. So this is a problem that will happen as long as you have a metadata class, it doesn't matter if it is nested or not.
Following that answer, you can register your metadata class with the following line (following your original example with the USER
class and the nested USER_Metadata
metadata class):
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(typeof(USER), typeof(USER.USER_Metadata)), typeof(USER));
Quick and dirty approach is to make sure that line is called before you validate the object, as in:
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(typeof(USER), typeof(USER.USER_Metadata)), typeof(USER));
var validationResults = new List<System.ComponentModel.DataAnnotations.ValidationResult>();
var isValid = Validator.TryValidateObject(
instance: user,
validationContext: new ValidationContext(user, serviceProvider: null, items: null),
validationResults: validationResults,
validateAllProperties: true);
However you could write some reflection code and register all the metadata classes in your assemblies, as part of your application initialization. Similar code was already written in this other SO question.
Hope it helps!
Upvotes: 3