Wherein I record a few tips on the use of System.ComponentModel.DataAnnotations, which I am likely to forget if I do not need to think about them again for some months…
Unit Testing for Validation Attributes
In your unit tests, do not test the validation — but rather test to see if the validation attributes have been applied. See Brad Wilson’s blog post, DataAnnotations and ASP.NET MVC, for details.
Manual Validation
If you do manually validate, in a unit test or production code, using
Validator.TryValidateObject
(or other methods in the Validator class), then be
sure to set the validateAllProperties = true
. By default it is false and that
means that only Required
properties will be validated. Validator.TryValidateObject(someObject, new ValidationContext(someObject, null,
null), resultList, true)
.
Validating Related Objects
Validating inherited fields works nicely. But what about composition / delegation? If you are validating object A, and it is composed of objects B and C that are also validated, then you need some “deep” validation. There is a simple recipe for accomplishing this:
- Implement
IValidatableObject
on class A, which will require you to add a method with signaturepublic IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
. -
Set this method to run
TryValidateObject
on the instance of B and of C. Each call toTryValidateObject
will need its ownValidationContext
. Constructing aValidationContext
is simple — just pass in the object itself, and generally it is appropriate to pass null for the second and third parameter. Don’t forget to throw in atrue
for validating all properties! Something like this will work (with opportunity for multiple refactorings):public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { var resultList = new List<ValidationResult>(); validationContextB = new ValidationContext(this.InstanceOfB, null, null); Validator.TryValidateObject(this.InstanceOfB, validationContextB, resultList, true); validationContextC = new ValidationContext(this.InstanceOfC, null, null); Validator.TryValidateObject(this.InstanceOfC, validationContextC, resultList, true); return resultList; }
- Now, you wrote a failing unit test for this Validate method before coding it, right?
Caution: if any of the properties directly in A are invalid, then those will be
detected and Validate(ValidationContext validationContext)
will never be
called.
Validation Summary in an MVC Partial View
When using partial views for AJAX support in an MVC application, a
@Html.ValidationSummary()
should be put inside the partial view. If you put it
inside the hosting view, then it will not be populated when you submit a form
inside the partial view.
Posted with : Tech, General Programming, Microsoft .NET Framework