Background
Hello all,
Basically, I am writing a custom data annotation in a .NET Core Class Library that will validate that an IEnumerable has at least a certain number of elements.
The Problem
For some reason, when running the validation, the IsValid is never called. I have already found a few other SO questions regarding this issue, but they all have a different problem than I do (basically, they weren't actually validating their objects). I am, however, validating my object (calling Validator.TryValidateObject(...)
) and yet, the IsValid is never called.
If I use any of the out-of-the-box validation attribute (e.g. Required), it operates as expected.
The Code
MinElementsAttribute.cs
public class MinElementsAttribute : ValidationAttribute
{
readonly int minElements;
public MinElementsAttribute(int minElements) : base($"Collection must have a size of at least {minElements}")
{
this.minElements = minElements;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var list = value as IEnumerable<object>;
if(list == null || list.Count() < this.minElements)
{
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}
}
MinElementsTests.cs
[Fact]
public void TestValidation()
{
var validationResults = new List<ValidationResult>();
var testObject = new TestObject();
// Should be false since I have not added anything to the list
var isValid = Validator.TryValidateObject(testObject, new ValidationContext(testObject), validationResults);
// Fails since isValid comes back as true because IsValid on MinElementsAttribute is never called
Assert.False(isValid);
Assert.NotEmpty(validationResults);
}
internal class TestObject
{
public TestObject()
{
this.StringList = new List<string>();
}
[MinElements(3)]
public List<string> StringList { get; set; }
}
Edit: The Solution
Please see the accepted answer for the solution. I wanted to add this to also note that by changing the inheritance to be from RequiredAttribute rather than ValidationAttribute, you can enforce the validation of all object properties implicitly.
Validator.TryValidateObject
class has a few overload methods. You want to use this one:
public static bool TryValidateObject(object instance, ValidationContext validationContext, ICollection<ValidationResult> validationResults, bool validateAllProperties);
From the MSDN article:
validateAllProperties
Type: System.Boolean
true to validate all properties; if false, only required attributes are validated.
You must use this method, because your attribute does not derive from the RequiredAttribute
class.
My Sample
var validationResults = new List<ValidationResult>();
var testObject = new TestObject();
ValidationContext contexts = new ValidationContext(testObject, null, null);
var isValid = Validator.TryValidateObject(testObject, contexts, validationResults, true);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With