Search code examples
c#enumsdata-annotationsxunit

How to handle Enums in C# with Data Annotations and testing it with XUnit


I would like to limit the value for my DTO.

I have an enum of months:

public enum Month
{
    January,
    February,
    March,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December
}

Now in my DTO I have:

public class MyDTO {
    public Month Month{ get; set; }
}

I need to use a Data Annotation in order to accept only From March to October, and test it. with Xunit as https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-dotnet-test

[Fact]
public void valid DTO_Months_ok()
{
    var dto = MyDTO()
    {
        Month = Month.March
    }
    var result = dto.validate();
    Assert.Equal(0, result.Count);
}

And check with Error

[Fact]
public void valid DTO_Months_Error()
{
    var dto = MyDTO()
    {
        Month = Month.February
    }
    var result = dto.validate();
    Assert.Equal(1, result.Count);
}

My problem, is with the DataAnnotation.

How to use DataAnnotation with Enum?


Solution

  • This looks a little "hacky" imho. DataAnnotations are rules that should be applied to a property of class, to prevent entering invalid values. That confirms the value will always be valid. Usually it is applied on values that "are too big" and "values that can be entered vary". As an example [StringLength(50, MinimumLength = 3)] where you limiting the length of a string.

    In case of enum: You don't want to use other values then from March to October, so why keep them in enum itself? From the question it appears that you want just to have those values, so it would make sense as:

    public enum RestrictedMonth
    {
        March,
        May,
        June,
        July,
        August,
        September,
        October,
    }
    

    Anyway, if that is not enough for you, you can define your custom data annotations with params as array:

    public class EnumInRangeAttribute : ValidationAttribute
    {
        public Month[] ValidMonths { get; set; }
    
        public EnumInRangeAttribute()
        {
            ValidMonths = new Month[0];
        }
    
        public EnumInRangeAttribute(params Month[] ValidMonths)
        {
            this.ValidMonths = ValidMonths;
        }
    
        public override bool IsValid(object value)
        {
            if (value is Month monthValue)
            {
                return ValidMonths.Contains(monthValue);
            }
            return true;
        }
    }
    
    public class MyDTO {
        [EnumInRange(Month.March,
            Month.May,
            Month.June,
            Month.July,
            Month.August,
            Month.September,
            Month.October)]
        public Month Month { get; set; }
    }