Search code examples
asp.net-mvcvalidationasp.net-mvc-2dto

An ASP.NET MVC validator to make sure at least one checkbox is checked


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?


Solution

  • 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.