Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

c# - Custom DataAnnotation IsValid Not Called

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...