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, for instance). How can I do it?
3 Answers
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);

- 1,093
- 1
- 20
- 38

- 19,064
- 3
- 47
- 70
-
I ended up writing a lot of reflection code to do this while I waited on an answer :( – Chris McCall Sep 24 '10 at 13:52
-
Yeah surprisingly few examples around using this API, and loads using reflection! – TheCodeKing Sep 24 '10 at 17:23
-
not a huge fan of output parameters, so I raised a custom exception instead (impossible to ignore) – Chris McCall Sep 26 '10 at 16:22
-
@TheCodeKing: most likely because there's loads of old examples for .NET 3.5 or older, where the number of validator attribute classes is really small and there's no ValidationContext nor Validation class. – Aoi Karasu Apr 05 '13 at 22:02
-
When initializing the VaildationContext why is it required to also specify the MemberName property? I found I kept getting ArgumentNullException when not specifying it. – nityan Jun 24 '14 at 00:23
-
3This is fantastic! For those interested, I use that in [this gist](https://gist.github.com/JimmyBoh/b7c135820c18a06648a5) as a simple helper class. – Jim Buck Aug 11 '14 at 18:03
-
Very Handy. I just added a few lines to my Business Object base class and I can use across all of my BO. Thanks Much! – pStan Mar 10 '15 at 14:18
-
7So i finally implemented this the right way this morning, 5 years after asking. Thanks man – Chris McCall Jun 03 '15 at 17:45
-
2What about nested objects / lists of objects / etc? This solution is non-recursive, but I think to be fully robust, it would need to be recursive. Anything built-in for that, or would you have to roll that manually? – gzak Aug 03 '17 at 01:34
-
2@gzak For nested items I built my own ValidateObjectAttribute that inherits from ValidationAttribute. See this gist https://gist.github.com/odyth/905bcaa84d257f663eb45a55481b660b – odyth Aug 21 '18 at 19:31
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.

- 101,809
- 122
- 424
- 632

- 70,210
- 21
- 112
- 164
-
Can I call the `TryValidateProperty` passing a random instance of `ValidationAttribute` to perform on? – Shimmy Weitzhandler Apr 18 '17 at 01:47
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;
}
}
}
}

- 36,764
- 19
- 160
- 190