[ACCEPTED]-Required Attribute on Generic List Property-generic-list

Accepted answer
Score: 35

Adding the Required attribute to a list-style property 18 doesn't really do what you want. The will 17 complain if the list isn't created, but 16 won't complain if the list exists with 0 15 item in it.

However, it should be easy enough 14 to derive your own data annotations attribute 13 and make it check the list for Count > 0. Something 12 like this (not tested yet):

[AttributeUsage(AttributeTargets.Property)]
public sealed class CannotBeEmptyAttribute : ValidationAttribute
{
    private const string defaultError = "'{0}' must have at least one element.";
    public CannotBeEmptyAttribute ( ) : base(defaultError) //
    { 
    }

    public override bool IsValid ( object value )
    {
      IList list = value as IList;
      return ( list != null && list.Count > 0 );
    }

    public override string FormatErrorMessage ( string name )
    {
        return String.Format(this.ErrorMessageString, name);
    }
}

EDIT:

You'll also 11 have to be careful how you bind your list 10 in your view. For example, if you bind a 9 List<String> to a view like this:

<input name="ListName[0]" type="text" />
<input name="ListName[1]" type="text" />
<input name="ListName[2]" type="text" />
<input name="ListName[3]" type="text" />
<input name="ListName[4]" type="text" />

The MVC model binder 8 will always put 5 elements in your list, all String.Empty. If 7 this is how your View works, your attribute 6 would need to get a bit more complex, such 5 as using Reflection to pull the generic 4 type parameter and comparing each list element 3 with default(T) or something.

A better alternative 2 is to use jQuery to create the input elements 1 dynamically.

Score: 27

For those who're looking for minimalist 7 examples:

[AttributeUsage(AttributeTargets.Property)]
public sealed class CannotBeEmptyAttribute : RequiredAttribute
{
    public override bool IsValid(object value)
    {
        var list = value as IEnumerable;
        return list != null && list.GetEnumerator().MoveNext();
    }
}

This is modified code from the 6 accepted answer. It is suitable in the case 5 from the question, and in even more cases, since 4 IEnumerable is higher in System.Collections 3 hierarchy. Additionally, it inherits behavior 2 from RequiredAttribute, so no need in coding 1 it explicitly.

Score: 5

For those that use C# 6.0 (and above) and 1 who are looking for one-liners:

[AttributeUsage(AttributeTargets.Property)]
public sealed class CannotBeEmptyAttribute : RequiredAttribute
{
    public override bool IsValid(object value) => (value as IEnumerable)?.GetEnumerator().MoveNext() ?? false;
}
Score: 3

Modified @moudrick implementation for my 3 requirement

Required Validation Attribute 2 for List and checkbox List

[AttributeUsage(AttributeTargets.Property)]
public sealed class CustomListRequiredAttribute : RequiredAttribute
{
    public override bool IsValid(object value)
    {
        var list = value as IEnumerable;
        return list != null && list.GetEnumerator().MoveNext();
    }
}

If you have checkbox 1 list

[AttributeUsage(AttributeTargets.Property)]
public sealed class CustomCheckBoxListRequiredAttribute : RequiredAttribute
{
    public override bool IsValid(object value)
    {
        bool result = false;

        var list = value as IEnumerable<CheckBoxViewModel>;
        if (list != null && list.GetEnumerator().MoveNext())
        {
            foreach (var item in list)
            {
                if (item.Checked)
                {
                    result = true;
                    break;
                }
            }
        }

        return result;
    }
}

Here is my View Model

public class CheckBoxViewModel
{        
    public string Name { get; set; }
    public bool Checked { get; set; }
}

Usage

[CustomListRequiredAttribute(ErrorMessage = "Required.")]
public IEnumerable<YourClass> YourClassList { get; set; }

[CustomCheckBoxListRequiredAttribute(ErrorMessage = "Required.")]
public IEnumerable<CheckBoxViewModel> CheckBoxRequiredList { get; set; }

More Related questions