I understand that IValidatableObject
is used to validate an object in a way that let's one compare properties against each other.
I'd still like to have attributes to validate individual properties, but I want to ignore failures on some properties in certain cases.
Am I trying to use it incorrectly in the case below? If not how do I implement this?
public class ValidateMe : IValidatableObject
{
[Required]
public bool Enable { get; set; }
[Range(1, 5)]
public int Prop1 { get; set; }
[Range(1, 5)]
public int Prop2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!this.Enable)
{
/* Return valid result here.
* I don't care if Prop1 and Prop2 are out of range
* if the whole object is not "enabled"
*/
}
else
{
/* Check if Prop1 and Prop2 meet their range requirements here
* and return accordingly.
*/
}
}
}
Quote from Jeff Handley's Blog Post on Validation Objects and Properties with Validator:
This indicates that what you are attempting to do won't work out-of-the-box because the validation will abort at step #2. You could try to create attributes that inherit from the built-in ones and specifically check for the presence of an enabled property (via an interface) before performing their normal validation. Alternatively, you could put all of the logic for validating the entity in the
Validate
method.First off, thanks to @paper1337 for pointing me to the right resources...I'm not registered so I can't vote him up, please do so if anybody else reads this.
Here's how to accomplish what I was trying to do.
Validatable class:
Using
Validator.TryValidateProperty()
will add to the results collection if there are failed validations. If there is not a failed validation then nothing will be add to the result collection which is an indication of success.Doing the validation:
It is important to set
validateAllProperties
to false for this method to work. WhenvalidateAllProperties
is false only properties with a[Required]
attribute are checked. This allows theIValidatableObject.Validate()
method handle the conditional validations.Just to add a couple of points:
Because the
Validate()
method signature returnsIEnumerable<>
, thatyield return
can be used to lazily generate the results - this is beneficial if some of the validation checks are IO or CPU intensive.Also, if you are using
MVC ModelState
, you can convert the validation result failures toModelState
entries as follows (this might be useful if you are doing the validation in a custom model binder):I liked cocogza's answer except that calling base.IsValid resulted in a stack overflow exception as it would re-enter the IsValid method again and again. So I modified it to be for a specific type of validation, in my case it was for an e-mail address.
This works much better! It doesn't crash and produces a nice error message. Hope this helps someone!
I implemented a general usage abstract class for validation
The problem with the accepted answer is that it now depends on the caller for the object to be properly validated. I would either remove the RangeAttribute and do the range validation inside the Validate method or I would create a custom attribute subclassing RangeAttribute that takes the name of the required property as an argument on the constructor.
For example: