I\'d like to use the data validation attributes in a library assembly, so that any consumer of the data can validate it without using a ModelBinder (in a console application
TryValidateProperty
is just badly written - you have to jump through hoops to get it to work outside a Controller, and even then, if you use it twice, it will end up quietly setting ModelState to valid/invalid and stop alterring that state, and stop returning accurate results from then on.
I gave up on it and just wrote my own validator. This'll loop over any set of objects in any context and tell you if they're valid:
bool isValid = true;
var invalidFields = new List<string>();
foreach (var o in viewModels)
{
var properties = o.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach(var prop in properties)
{
var attrs = prop.GetCustomAttributes(true);
if (attrs != null)
{
var val = prop.GetValue(o);
ValidationAttribute[] validatorAttrs = attrs
.Where(a => a is ValidationAttribute)
.Select(a => (ValidationAttribute)a).ToArray();
foreach(var attr in validatorAttrs)
{
bool thisFieldIsValid = attr.IsValid(val);
if (!thisFieldIsValid)
invalidFields.Add(prop.Name);
isValid = isValid && thisFieldIsValid;
}
}
}
}
Actually this is pretty cool. I used it in a WFP validation implementation recently. Most people end up writing lots of code using reflection to iterate the attributes, but there's a built in function for this.
var vc = new ValidationContext(myObject, null, null);
return Validator.TryValidateObject(myObject, vc, null, true);
You can also validate attributes on a single named property. You can also optionally pass in a list in order to access the error messages :
var results = new List<ValidationResult>();
var vc = new ValidationContext(myObject, null, null) { MemberName = "UserName"};
var isValid = Validator.TryValidateProperty(value, vc, results);
// get all the errors
var errors = Array.ConvertAll(results.ToArray(), o => o.ErrorMessage);
The System.ComponentModel.DataAnnotations.ValidationAttribute
classes have IsValid
methods that perform the validation logic. They take an Object (the value of the field they decorate) and return true
or false
.
You can use these attributes, and a little reflection, to roll your own aspect-oriented validator. Pass your validator an object, and the validator will get a list of PropertyInfo
s and FieldInfo
s. For each of these, it can call GetCustomAttributes
to look for those that inherit from ValidationAttribute
, and for each of these, call IsValid
, passing the value of the property or field. This can be done totally dynamically without knowing the structure of the class to be validated at design-time.