I have an ASP.NET MVC 2 project in which I've created a data transfer object to receive data from a web page form. The form has two groups of checkboxes on it. I want to validate the object to make sure that at least one of the checkboxes in each group is checked.
I'm doing the validation on the server side so that a user won't be able to hack around any client-side validation. (I will add client-side validation with jQuery later; that's easy.)
My understanding is that I have to create my own custom ValidationAttribute for my data transfer object class, but I don't understand how to create and use one that can accept an arbitrary list of checkbox properties to make sure that at least one of them is true. I am guessing I will have to call the attributes like this:
[AtLeastOneCheckbox("set1check1", "set1check2", "set1check3",
ErrorMessage = "You must check at least one checkbox in set 1.")]
[AtLeastOneCheckbox("set2check1", "set2check2", "set2check3", "set2check4", "set2check5",
ErrorMessage = "You must check at least one checkbox in set 2.")]
public class MyFormDTO
{
...
}
What would the implementation of AtLeastOneCheckboxAttribute look like?
Or is there a different way that I should do this kind of validation?
if you have several checkbox groups, you just need to deine the attribute several times.
[AttributeUsage( AttributeTargets.Class)]
public class AtLeastOneCheckboxAttribute : ValidationAttribute
{
private string[] _checkboxNames;
public AtLeastOneCheckboxAttribute(params string[] checkboxNames)
{
_checkboxNames = checkboxNames;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var propertyInfos = value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x=>_checkboxNames.Contains(x.Name));
var values = propertyInfos.Select(x => x.GetGetMethod().Invoke(value, null));
if (values.Any(x => Convert.ToBoolean(x)))
return ValidationResult.Success;
else
{
ErrorMessage = "At least one checkbox must be selected";
return new ValidationResult(ErrorMessage);
}
}
}
UPDATE
as you have found out, class-level validation is called only after all properties pass. In order to get the error, just use empty string as the key.