Перевірка ViewModel для списку


76

У мене є таке визначення моделі перегляду

public class AccessRequestViewModel
{
    public Request Request { get; private set; }
    public SelectList Buildings { get; private set; }
    public List<Person> Persons { get; private set; }
}

Отже, у моїй заявці має бути принаймні 1 людина для запиту на доступ. Який підхід ви можете використати для перевірки? Я не хочу, щоб така перевірка відбувалась у моєму контролері, що було б просто зробити. Є єдиним вибором спеціальний атрибут перевірки?

Редагувати: На даний момент виконується ця перевірка за допомогою FluentValidation (приємна бібліотека!)

RuleFor(vm => vm.Persons)
                .Must((vm, person) => person.Count > 0)
                .WithMessage("At least one person is required");

Відповіді:


172

Якщо ви використовуєте Анотації даних для перевірки, вам може знадобитися спеціальний атрибут:

public class EnsureOneElementAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        var list = value as IList;
        if (list != null)
        {
            return list.Count > 0;
        }
        return false;
    }
}

і потім:

[EnsureOneElement(ErrorMessage = "At least a person is required")]
public List<Person> Persons { get; private set; }

або щоб зробити його більш загальним:

public class EnsureMinimumElementsAttribute : ValidationAttribute
{
    private readonly int _minElements;
    public EnsureMinimumElementsAttribute(int minElements)
    {
        _minElements = minElements;
    }

    public override bool IsValid(object value)
    {
        var list = value as IList;
        if (list != null)
        {
            return list.Count >= _minElements;
        }
        return false;
    }
}

і потім:

[EnsureMinimumElements(1, ErrorMessage = "At least a person is required")]
public List<Person> Persons { get; private set; }

Особисто я використовую FluentValidation.NET замість Анотацій даних для виконання перевірки, оскільки я віддаю перевагу імперативній логіці перевірки замість декларативної. Я думаю, що це потужніше. Тож моє правило перевірки буде просто виглядати так:

RuleFor(x => x.Persons)
    .Must(x => x.Count > 0)
    .WithMessage("At least a person is required");

Схоже, мені потрібно використовувати перевантаження Must () для використання осіб. Підрахуйте, будь ласка, перегляньте мою редакцію та повідомте мене, якщо у вас є версія, яка є більш
дружньою

1
@ryan, насправді є два перевантаження цього методу, як показано в документації . Тож моя версія більш дружня. Не хвилюйтеся, якщо Visual Studio підкреслює це як помилку. Це має спрацювати, якщо ви намагаєтесь скомпілювати. Просто VS Intellisense недостатньо просунутий, щоб зрозуміти це :-) Тож RuleFor(x => x.Persons).Must(x => x.Count > 0).WithMessage("At least a person is required");буде компілювати і працювати нормально.
Дарін Димитров

Дивно, але зараз це не підкреслюється. Дякую!
Райан

Дякую, Дарін, ти вибрав класну назву атрибута (: Просто цікаво, чи використовуєш ти будь-яку систему перевірки і на стороні клієнта . Вбудовані анотації даних забезпечують перевагу на стороні клієнта, якщо хтось вважає це корисним.
Саро Ташіян

Як я повинен використовувати його в бритві? Якщо я пишу @foreach (var p у Model.Person) {...} @ Html.ValidationMessageFor (model => Model.Person), викликається метод сервера перевірки, але повідомлення про перевірку не відображається.
Кейт

18

Іншим можливим способом обробки перевірки кількості для членів колекції об'єкта подання є наявність обчислюваної властивості, що повертає кількість колекції або списку. Потім RangeAttribute можна застосувати, як у коді нижче, щоб забезпечити перевірку підрахунку:

[Range(minimum: 1, maximum: Int32.MaxValue, ErrorMessage = "At least one item needs to be selected")]
public int ItemCount
{
    get
    {
        return Items != null ? Items.Length : 0;
    }
}

У наведеному вище коді ItemCount є прикладом обчислюваної властивості на моделі подання, що перевіряється, а Items - прикладом властивості колекції членів, кількість яких перевіряється. У цьому прикладі принаймні один елемент застосовується до елемента колекції, а максимальний ліміт - це максимальне значення, яке може приймати ціле число, яке для більшості практичних цілей є необмеженим. Повідомлення про помилку про помилку перевірки також можна встановити через член ErrorMessage у RangeAttribute у прикладі вище.


14

Наступний код працює в ядрі asp.net 1.1.

[Required, MinLength(1, ErrorMessage = "At least one item required in work order")]
public ICollection<WorkOrderItem> Items { get; set; }

1
Здається, це більше не працює в .NET Core 2.1.0 (Попередній перегляд 1)
Свен,

3
Це працює в .Net Core 2.2, я спробував це на типі List. Якщо ви хочете окреме спеціальне повідомлення для атрибута [Обов’язково], це теж можливо. [Обов’язково (ErrorMessage = "Відсутні вимірювальні прилади"), MinLength (1, ErrorMessage = "Потрібно щонайменше 1 метр")] загальнодоступний список <Meter> метра {get; встановити; }
Jeshwel

8

Відповідь Дарін хороша, але версія нижче автоматично дасть вам корисне повідомлення про помилку.

public class MinimumElementsAttribute : ValidationAttribute
{
    private readonly int minElements;

    public MinimumElementsAttribute(int minElements)
    {
        this.minElements = minElements;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var list = value as IList;

        var result = list?.Count >= minElements;

        return result
            ? ValidationResult.Success
            : new ValidationResult($"{validationContext.DisplayName} requires at least {minElements} element" + (minElements > 1 ? "s" : string.Empty));
    }
}

Використання:

[MinimumElements(1)]
public List<Customer> Customers {get;set}

[MinimumElements(2)]
public List<Address> Addresses {get;set}

Повідомлення про помилку:

  • Клієнтам потрібно принаймні 1 елемент
  • Адреси вимагають принаймні 2 елементи

2

Тут у вас є два варіанти: або створити власний атрибут перевірки та прикрасити ним властивість, або ви можете змусити ваш ViewModel реалізувати IValidatableObjectінтерфейс (який визначає Validateметод)

Сподіваюся, це допомагає :)


0

Було б дуже чисто та елегантно мати спеціальну перевірку. Щось на зразок цього:

public class AccessRequestViewModel
{
    public Request Request { get; private set; }
    public SelectList Buildings { get; private set; }
    [AtLeastOneItem]
    public List<Person> Persons { get; private set; }
}

Або [MinimumItems(1)].


0

Одним із підходів може бути використання приватного конструктора та статичного методу для повернення екземпляра об’єкта.

public class AccessRequestViewModel
{
    private AccessRequesetViewModel() { };

    public static GetAccessRequestViewModel (List<Person> persons)
    {
            return new AccessRequestViewModel()
            {
                Persons = persons,
            };
    }

    public Request Request { get; private set; }
    public SelectList Buildings { get; private set; }
    public List<Person> Persons { get; private set; }
}

Завжди використовуючи фабрику для створення екземпляра ViewModel, ви можете переконатися, що людина завжди знайдеться.

Це, мабуть, не ідеально для того, що ви хочете, але, швидше за все, це спрацює.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.