Search code examples
c#asp.netvalidationannotations

How can I use the ASP.NET [Range] annotation for IEnumerable elements?


I want to use the ASP.NET [Range] Annotation but for the elements IEnumerables.

I used the existing RangeAttribute like this:

public class RangeEnumerable : RangeAttribute
{
    /// <inheritdoc/>
    public RangeEnumerable(double minimum, double maximum) : base(minimum, maximum)
    {
    }

    /// <inheritdoc/>
    public RangeEnumerable(int minimum, int maximum) : base(minimum, maximum)
    {
    }

    /// <inheritdoc/>
    public RangeEnumerable([DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type type, string minimum, string maximum) : base(type, minimum, maximum)
    {
    }

    /// <inheritdoc/>
    public override bool IsValid(object? value)
    {
        if (null == value) { return true; } 

        IEnumerable<object> list = ((IEnumerable)value).Cast<object>();
        
        foreach (object item in list)
        {
            if (!base.IsValid(item))
            {
                return false;
            }                
        }

        return true;
    }
}

and annotated my Parameter like this:

[RangeEnumerable(MINIMUM_ANGLE, MAXIMUM_ANGLE)]
public IEnumerable<Double> PhaseAnglesVoltage { get; set; } = new List<double>();

And wrote the following unit test:

[Test]
public void TestInvalidPhaseAngleVoltageTooLow()
{
    // Arrange       
    Loadpoint loadpoint1 = new Loadpoint();
    loadpoint1.PhaseAnglesVoltage.Append(-1);

    // Act
    var errCount = ValidateObject(loadpoint1);

    // Assert
    Assert.AreEqual(1, errCount);
}

private int ValidateObject(object obj)
{
    var validationContext = new ValidationContext(obj, null, null);
    var validationResults = new List<ValidationResult>();
    Validator.TryValidateObject(obj, validationContext, validationResults, true);
    return validationResults.Count;
}

I expected the loop to iterate over the elements of the List I used the annotation with, but in the IsValid-Function I always get an empty List instead of one with the element appended in the test.


Solution

  • Ok, I've found the error, which was in the unit test. IEnumerable.Append doesn't add the element to the original object like List.Add does (see Difference between a List's Add and Append method?).

    Changing the unit test to the following does the trick.

            [Test]
            public void TestInvalidPhaseAngleVoltageTooLow()
            {
                // Arrange       
                Loadpoint loadpoint1 = new Loadpoint();
                loadpoint1.PhaseAnglesVoltage = loadpoint1.PhaseAnglesVoltage.Append(-1);
    
                // Act
                var errCount = ValidateObject(loadpoint1);
       
                // Assert
                Assert.AreEqual(1, errCount);
            }