65.9K
CodeProject is changing. Read more.
Home

Custom Validation Attribute – Contains

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

Aug 3, 2015

CPOL

1 min read

viewsIcon

5910

Custom validation attribute - contains

Situation

I needed to create a custom validation attribute. More specifically, contains attribute.

I have a a few properties where I need to know if input value is valid and valid = value is contained in a list of allowed values.

For example:

property: title

allowed values: Mr, Mrs, Miss, Dr, etc.

So I created my own custom validation attribute:

public class ContainsValidatorAttribute : ValidationAttribute
{
    public string[] AcceptedValues { get; set; }
    public override bool IsValid(object value)
    {
        string strValue = value as string;
        if (!AcceptedValues.Contains(strValue))
        {
            return false;
        }
        return true;
    }
}
 
//Usage:
[ContainsValidator(AcceptedValues = new[] { "Mr", "Mrs", "Miss", "Ms", "Dr" }, 
ErrorMessage = "Not a valid title")]
public string Title { get; set; }

This works great, but then I thought it would be much better to have all the values in one place rather than write them right into an attribute.

So I created a static helper class with all allowed values:

public static class ValidatorHelper
{
    public static readonly string[] Titles = { "Mr", "Mrs", "Miss", "Ms", "Dr" };
    public static readonly string[] SmokerStatus = { "Yes", "No", "Ex"};
}

However, this will not work, because you cannot pass a static value into an attribute like this:

[ContainsValidator(AcceptedValues = ValidatorHelper.Titles, ErrorMessage = "Not a valid title")]
public string Title { get; set; }

This will end with error even if you make string[] AcceptedValues static.

My Solution

public class ContainsValidatorAttribute : ValidationAttribute
{
    public string AcceptedValuesKey { get; set; }
    public override bool IsValid(object value)
    {
        var strValue = (string) value;
        string[] values;
        ValidatorHelper.Dictionary.TryGetValue(AcceptedValuesKey, out values);
        return values != null && values.Contains(strValue);
    }
}

and store values in a dictionary:

public static class ValidatorHelper
{
    public static readonly string[] Titles = { "Mr", "Mrs", "Miss", "Ms", "Dr" };
    public static readonly string[] SmokerStatus = { "Yes", "No", "Ex"};

    public const string TitleKey = "Title";
    public const string SmokerStatusKey = "Smoke";

    public static Dictionary<string, string[]> Dictionary = new Dictionary<string, string[]>()
    {
        {TitleKey, Titles},
        {SmokerStatusKey, SmokerStatus},
    };
}

Now you can store all values in one place and use dictionary to get allowed values.

[ContainsValidator(AcceptedValuesKey = ValidatorHelper.TitleKey, ErrorMessage = "Not a valid title")]
public string Title { get; set; }

And here is the unit test.

[TestClass]
    public class ContainsAttributeTest
    {
        [TestMethod]
        public void TestContainsAttribute()
        {
            var attr = new ContainsValidatorAttribute();
            attr.AcceptedValuesKey = ValidatorHelper.TitleKey;
            var result = attr.IsValid("Mr");

            Assert.AreEqual(true, result);
        }

        [TestMethod]
        public void TestContainsAttributeFailNull()
        {
            var attr = new ContainsValidatorAttribute();
            attr.AcceptedValuesKey = "invalid key";
            var result = attr.IsValid("Mr");

            Assert.AreEqual(false, result);
        }

        [TestMethod]
        public void TestContainsAttributeFailInvalidValue()
        {
            var attr = new ContainsValidatorAttribute();
            attr.AcceptedValuesKey = ValidatorHelper.TitleKey;
            var result = attr.IsValid("invalid");

            Assert.AreEqual(false, result);
        }
    }

Summary

This is not an ideal solution, but it is the only one I was able to come up with.

If you have any ideas on how to improve this solution, I would be grateful if you could share it with us in the comments.