Можно ли поместить атрибут [Обязательный] в свойство List < > ?
Я связываюсь с общим списком в POST и задавался вопросом, могу ли я сделать ModelState.IsValid(), если свойство имеет в нем 0 элементов?
Можно ли поместить атрибут [Обязательный] в свойство List < > ?
Я связываюсь с общим списком в POST и задавался вопросом, могу ли я сделать ModelState.IsValid(), если свойство имеет в нем 0 элементов?
Добавление атрибута Required
в свойство стиля списка действительно не делает то, что вы хотите. Будет жаловаться, если список не создан, но не будет жаловаться, если список существует с 0 элементом в нем.
Однако, должно быть достаточно легко получить свой собственный атрибут аннотаций данных и заставить его проверить список для Count
> 0. Что-то вроде этого (еще не проверено):
[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:
Вам также нужно быть осторожным, как вы привязываете свой список в своем представлении. Например, если вы привязываете List<String>
к виду, подобному этому:
<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" />
Связывание модели MVC будет всегда помещать 5 элементов в ваш список, все String.Empty
. Если это работает в вашем представлении, вашему атрибуту нужно будет немного усложниться, например, используя Reflection, чтобы вытащить параметр типового типа и сравнить каждый элемент списка с default(T)
или чем-то.
Лучшей альтернативой является использование jQuery для динамического создания входных элементов.
Для тех, кто ищет минималистские примеры:
[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();
}
}
Это измененный код из принятого ответа. Он подходит в этом случае из вопроса и в еще большем числе случаев, поскольку IEnumerable выше в иерархии System.Collections. Кроме того, он наследует поведение от RequiredAttribute, поэтому нет необходимости в его кодировании явно.
Модифицированная реализация @moudrick для моего требования
Обязательный атрибут проверки для списка и списка флажков
[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();
}
}
Если у вас есть список флажков
[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;
}
}
Вот моя модель просмотра
public class CheckBoxViewModel
{
public string Name { get; set; }
public bool Checked { get; set; }
}
Использование
[CustomListRequiredAttribute(ErrorMessage = "Required.")]
public IEnumerable<YourClass> YourClassList { get; set; }
[CustomCheckBoxListRequiredAttribute(ErrorMessage = "Required.")]
public IEnumerable<CheckBoxViewModel> CheckBoxRequiredList { get; set; }
Для тех, кто использует С# 6.0 и кто ищет однострочные:
[AttributeUsage(AttributeTargets.Property)]
public sealed class CannotBeEmptyAttribute : RequiredAttribute
{
public override bool IsValid(object value) => (value as IEnumerable)?.GetEnumerator().MoveNext() ?? false;
}