23

C# enum values are not limited to only values listed in it's definition and may store any value of it's base type. If base type is not defined than Int32 or simply int is used.

I am developing a WCF serivice which needs to be confident that some enum has a value assigned as opposed to the default value for all enums of 0. I start with a unit test to find out whether [Required] would do right job here.

using System.ComponentModel.DataAnnotations;
using Xunit;

public enum MyEnum
{
    // I always start from 1 in order to distinct first value from the default value
    First = 1,
    Second,
}

public class Entity
{
    [Required]
    public MyEnum EnumValue { get; set; }
}

public class EntityValidationTests
{
    [Fact]
    public void TestValidEnumValue()
    {
        Entity entity = new Entity { EnumValue = MyEnum.First };

        Validator.ValidateObject(entity, new ValidationContext(entity, null, null));
    }

    [Fact]
    public void TestInvalidEnumValue()
    {
        Entity entity = new Entity { EnumValue = (MyEnum)(-126) };
        // -126 is stored in the entity.EnumValue property

        Assert.Throws<ValidationException>(() =>
            Validator.ValidateObject(entity, new ValidationContext(entity, null, null)));
    }
}

It does not, the second test does not throw any exception.

My question is: is there a validator attribute to check that supplied value is in Enum.GetValues?

Update. Make sure to use the ValidateObject(Object, ValidationContext, Boolean) with last parameter equal to True instead of ValidateObject(Object, ValidationContext) as done in my unit test.

Mike
  • 2,468
  • 3
  • 25
  • 36
  • I never used it, so I don't know if it checks for the values, but you could try EnumDataTypeAttribute : http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.enumdatatypeattribute%28v=vs.95%29.aspx – Raphaël Althaus Jan 17 '13 at 14:53

3 Answers3

35

There's EnumDataType in .NET4+ ...

Make sure you set the 3rd parameter, validateAllProperties=true in the call to ValidateObject

so from your example:

public class Entity
{
    [EnumDataType(typeof(MyEnum))]
    public MyEnum EnumValue { get; set; }
}

[Fact]
public void TestInvalidEnumValue()
{
    Entity entity = new Entity { EnumValue = (MyEnum)(-126) };
    // -126 is stored in the entity.EnumValue property

    Assert.Throws<ValidationException>(() =>
        Validator.ValidateObject(entity, new ValidationContext(entity, null, null), true));
}
qujck
  • 14,388
  • 4
  • 45
  • 74
  • Just switched from `[Required]` to `[EnumDataType(typeof(MyEnum))]` and tested, still no `ValidationException` – Mike Jan 17 '13 at 15:12
  • Have you tried adding true as the 3rd parameter to the ValidateObject call? – qujck Jan 17 '13 at 15:23
  • Good point! Now it works. There's only `[EnumDataType(typeof(MyEnum))]`, no `[Required]` and I call `ValidateObject` with `validateAllProperties=true`. Could you please update your answer showing the correct validation approach so I could accept it? – Mike Jan 17 '13 at 15:54
11

What you're looking for is:

 Enum.IsDefined(typeof(MyEnum), entity.EnumValue)

[Update+1]

The out of the box validator that does a lot of validations including this one is called EnumDataType. Make sure you set validateAllProperties=true as ValidateObject, otherwise your test will fail.

If you just want to check if the enum is defined, you can use a custom validator with the above line:

    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)]
    public sealed class EnumValidateExistsAttribute : DataTypeAttribute
    {
        public EnumValidateExistsAttribute(Type enumType)
            : base("Enumeration")
        {
            this.EnumType = enumType;
        }

        public override bool IsValid(object value)
        {
            if (this.EnumType == null)
            {
                throw new InvalidOperationException("Type cannot be null");
            }
            if (!this.EnumType.IsEnum)
            {
                throw new InvalidOperationException("Type must be an enum");
            }
            if (!Enum.IsDefined(EnumType, value))
            {
                return false;
            }
            return true;
        }

        public Type EnumType
        {
            get;
            set;
        }
    }

... but I suppose it's not out of the box then is it?

atlaste
  • 30,418
  • 3
  • 57
  • 87
1

I solved my need simply based on this website https://kristinaalberto.com/making-enum-parameters-required-in-asp-net-core-mvc/

    public enum CreatedBySelfOrOthersEnumValues
    {
        Self,
        Others
    }

  
    public class CampaignRegisterValidationModel
    {
        [Required]
        public string Name { get; set; }

        [Required]
        public CreatedBySelfOrOthersEnumValues CreatedForSelfOrOthers { get; set; }

        [Required]
        public int CountryCode { get; set; }

        public string Phone { get; set; }
        public string Email { get; set; }
    }

Then validating

     if (ModelState.IsValid)
     {
     }
Arun Prasad E S
  • 9,489
  • 8
  • 74
  • 87