Is it possible to put a [Required] attribute onto a List<> property?
I bind to a generic list on POST and was wondering if I could make ModelState.IsValid() fail if the property has 0 items in it?
Is it possible to put a [Required] attribute onto a List<> property?
I bind to a generic list on POST and was wondering if I could make ModelState.IsValid() fail if the property has 0 items in it?
Adding the Required
attribute to a list-style property doesn't really do what you want. The will complain if the list isn't created, but won't complain if the list exists with 0 item in it.
However, it should be easy enough to derive your own data annotations attribute and make it check the list for Count
> 0. Something 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 have to be careful how you bind your list in your view. For example, if you bind a 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 will always put 5 elements in your list, all String.Empty
. If this is how your View works, your attribute would need to get a bit more complex, such as using Reflection to pull the generic type parameter and comparing each list element with default(T)
or something.
A better alternative is to use jQuery to create the input elements dynamically.
For those who're looking for minimalist 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 accepted answer. It is suitable in the case from the question, and in even more cases, since IEnumerable is higher in System.Collections hierarchy. Additionally, it inherits behavior from RequiredAttribute, so no need in coding it explicitly.
base.IsValid(value)
as well so that base class behavior is maintained. For C# 8's users, this can be as simple as return base.IsValid(value) && value is IEnumerable seq && seq.GetEnumerator().MoveNext();
–
Raveaux For those that use C# 6.0 (and above) and 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;
}
Modified @moudrick implementation for my requirement
Required Validation Attribute 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 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; }
© 2022 - 2024 — McMap. All rights reserved.