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;
}
}
[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([__strong__]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.