1

I am creating a Web API in MVC. In my ViewModel-Objects I want to create validations for integer inputs, which are later in the process mapped to some enums.

Note: I can not change the type of the view model to an actual enum because of restrictions outside of the scope of my project.

Here's what I have:

[ClientValidation]
public class ContactDataObject {
    [Range(1,3)] //fixed range, bad
    public int? SalutationCd { get; set; }
}

And I could also do

    [Range(/*min*/(int)Salutation.Mr, /*max*/(int)Salutation.LadiesAndGentlemen)]

This works fine, we have 3 variants of salutations right now. However, since I already know that this is later mapped to an enum, I would like to do something like this See [EnumDataTypeAttribute Class][1].

[ClientValidation]
public class ContactDataObject {
    [EnumDataType(typeof(Salutation))] //gives mapping error
    public int? SalutationCd { get; set; }
}

However, with this give a mapping error.

I would like to have an attribute, that validates only whether my interger is within the values of the given enum. How to validate for the (integer) values of the enum?

[1]: https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.enumdatatypeattribute?view=net-5.0):

Marcel
  • 15,039
  • 20
  • 92
  • 150
  • 1
    i'd write my own [`ValidationAttribute`](https://learn.microsoft.com/en-gb/dotnet/api/system.componentmodel.dataannotations.validationattribute?view=net-5.0) that checks if the value can be cast to a specific enum type – Franz Gleichmann Jan 25 '21 at 16:06
  • try getting length of enum types using this `Enum.GetNames(typeof(MyEnum)).Length` – Krishna Chaitanya Jan 25 '21 at 16:08
  • 1
    @KrishnaChaitanya that might fail if the enum is declared with explicit values and has gaps – Franz Gleichmann Jan 25 '21 at 16:09
  • @KrishnaChaitanya The values given for the range expression must be constants, thus this dynamic approach does not work – Marcel Jan 25 '21 at 16:11
  • Length gives the number of elements in the enum, regardless of the gaps in the enum – Krishna Chaitanya Jan 25 '21 at 16:20
  • @Marcel - what about you define your property as `[EnumDataType(typeof(Salutation))] [JsonProperty("SalutationCd")]public Salutation? Salutation{ get; set; }` and another read only property with `JsonIgnore which should be used in your code [JsonIgnore]public int? SalutationCd{ get { ...return from here.....} }` – user1672994 Jan 25 '21 at 16:30

3 Answers3

1

You can try using custom validation:

public class EnumValueValidationAttribute : ValidationAttribute {
    private readonly Type _enumType;

    public EnumValueValidationAttribute(Type type) {
        _enumType = type;
    }

    public override bool IsValid(object value) {
        return value != null && Enum.IsDefined(_enumType, value); //null is not considered valid
    }
}

Then use it like:

    [EnumValueValidation(typeof(Salutation))]
    public int? SalutationCd { get; set; }
Marcel
  • 15,039
  • 20
  • 92
  • 150
  • You can't do this per the comment of Franz. The enum could be define 1,2,4,8. You just check the length, assuing an enum is always 0,1,2,3,... You need to check that the value being checked is actually a value of the enum. Not just a length check – Fran Jan 25 '21 at 16:19
  • @Fran is right, but the approach seems genrally vaild. I guess I should enumerate the enumeration to check, not just try to get the lenght, I guess? – Marcel Jan 25 '21 at 16:22
  • As per my understanding, lenght gives the enum length regardless of gaps in enum. Let me give a try in my machine and Ill get back. – Krishna Chaitanya Jan 25 '21 at 16:23
  • @KrishnaChaitanya I guess you should have a look at https://stackoverflow.com/a/2674751/79485. Would you incorporate that and then it looks like a valid answer.? – Marcel Jan 25 '21 at 16:25
  • I just did `int length = Enum.GetNames(typeof(MyEnum)).Length;` with Enum values public enum MyEnum { a1 = 1, b1 = 12, c1 = 13 } I got length as 3 – Krishna Chaitanya Jan 25 '21 at 16:26
  • Check it now @Marcel , I have edited the answer, Now its using Enum.IsDefined – Krishna Chaitanya Jan 25 '21 at 16:31
  • 1
    @KrishnaChaitanya I added my actual code which is very much like yours, including the type and an usage example. If no one comes up with a better solution in 24h, i am going to accept this answer – Marcel Jan 25 '21 at 16:48
  • @Marcel looks like no one came up with a alternative solution, can you approve my solution. – Krishna Chaitanya Jan 26 '21 at 17:39
0

This class lets you map the underlying value in a column to a corresponding enumeration constant name. This lets you define an enumeration that contains descriptive values that correspond to database values, and then use the enumeration constant names instead of the database values when data is displayed.

[EnumDataType(typeof(ReorderLevel))]  
public object SalutationCd { get; set; }  
MD. RAKIB HASAN
  • 3,670
  • 4
  • 22
  • 35
  • Yes, I have seen that (See my link), but unfortunately it does not work with an integer property. – Marcel Jan 25 '21 at 16:20
0
public class EnumValidation : ValidationAttribute
{
    private readonly Type type;

    public EnumValidation(Type type)
    {
        this.type = type;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        string message = FormatErrorMessage(validationContext.DisplayName);

        if (value == null)
            return ValidationResult.Success;

        try
        {
            if (!Enum.IsDefined(type, (int)value))
                return new ValidationResult(message);
        }
        catch (Exception ex)
        {
            return new ValidationResult(message);
        }

        return ValidationResult.Success;
    }
}

[EnumValidation(type:typeof(MemberTypeEnum), ErrorMessageResourceType = typeof(Message), ErrorMessageResourceName = "ERR_Invalid")]
Sven Eberth
  • 3,057
  • 12
  • 24
  • 29
Milad
  • 31
  • 4