ASP.NET MVC: настраиваемая проверка с помощью DataAnnotation

110

У меня есть модель с 4 свойствами, которые имеют тип строки. Я знаю, что вы можете проверить длину отдельного свойства с помощью аннотации StringLength. Однако я хочу проверить длину 4 свойств вместе взятых.

Как с помощью MVC сделать это с аннотацией данных?

Я спрашиваю об этом, потому что я новичок в MVC и хочу сделать это правильно, прежде чем принимать собственное решение.

Дэнни ван дер Краан
источник
2
Вы смотрели на Fluent Validation? Он обрабатывает сложные сценарии намного лучше, чем аннотации к данным
levelnis
Посмотрите на высоко рекомендуемых решений .... dotnetcurry.com/ShowArticle.aspx?ID=776
Niks
Спасибо за ответы. Я проверю Fluent Validation, никогда о ней не слышал. И Никс, Дарин в основном написал то, что объяснялось в статье по опубликованной вами ссылке. Итак, спасибо ... Отличный материал!
Дэнни ван дер Краан

Ответы:

177

Вы можете написать собственный атрибут проверки:

public class CombinedMinLengthAttribute: ValidationAttribute
{
    public CombinedMinLengthAttribute(int minLength, params string[] propertyNames)
    {
        this.PropertyNames = propertyNames;
        this.MinLength = minLength;
    }

    public string[] PropertyNames { get; private set; }
    public int MinLength { get; private set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var properties = this.PropertyNames.Select(validationContext.ObjectType.GetProperty);
        var values = properties.Select(p => p.GetValue(validationContext.ObjectInstance, null)).OfType<string>();
        var totalLength = values.Sum(x => x.Length) + Convert.ToString(value).Length;
        if (totalLength < this.MinLength)
        {
            return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
        }
        return null;
    }
}

а затем у вас может быть модель представления и украсить ею одно из ее свойств:

public class MyViewModel
{
    [CombinedMinLength(20, "Bar", "Baz", ErrorMessage = "The combined minimum length of the Foo, Bar and Baz properties should be longer than 20")]
    public string Foo { get; set; }
    public string Bar { get; set; }
    public string Baz { get; set; }
}
Дарин Димитров
источник
4
Спасибо за ответ, я принял ваш ответ. На самом деле чувствую себя немного смущенным. Вы выписали все решение! Хехе. Только нужно было изменить функцию IsValid, чтобы проверить максимальную длину. Так является ли это принятым решением MVC для подобных проблем?
Дэнни ван дер Краан
7
@DannyvanderKraan, да, это приемлемый способ. Конечно, это настолько отстой, что я никогда не использую его и вместо этого использую FluentValidation.NET для проверки.
Дарин Димитров
11
Здесь: fluentvalidation.codeplex.com . Вы могли бы просто написал простой валидатор для модели представления , которые могли бы выглядеть так (одна строка кода): this.RuleFor(x => x.Foo).Must((x, foo) => x.Foo.Length + x.Bar.Length + x.Baz.Length < 20).WithMessage("The combined minimum length of the Foo, Bar and Baz properties should be longer than 20");. Теперь посмотрите на код в моем ответе, который вам нужно написать с аннотациями данных, и скажите, какой из них вы предпочитаете. Модель декларативной проверки очень плохая по сравнению с императивной моделью.
Дарин Димитров
1
Это немного поздно, но кто-нибудь знает, есть ли другой параметр, который вы должны «включить», чтобы разрешить пользовательские аннотации данных? Я знаю о добавлении пространства имен для ненавязчивых js в файл web.config, но где-нибудь еще?
Jose
1
Я искал это все утро! Я реализовал его, и, к сожалению, когда IsValidон вызван, validationContextимеет значение null. Есть идеи, что я сделал не так? :-(
Grimm The Opiner
96

Самостоятельная проверка модели

Ваша модель должна реализовывать интерфейс IValidatableObject. Поместите свой код проверки в Validateметод:

public class MyModel : IValidatableObject
{
    public string Title { get; set; }
    public string Description { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (Title == null)
            yield return new ValidationResult("*", new [] { nameof(Title) });

        if (Description == null)
            yield return new ValidationResult("*", new [] { nameof(Description) });
    }
}

Обратите внимание: это проверка на стороне сервера . На стороне клиента это не работает. Ваша проверка будет выполнена только после отправки формы.

Андрей
источник
Спасибо, что ответил, Андрей. Хотя ваше решение тоже подойдет, я выбрал решение Дарина, потому что оно более многоразовое.
Дэнни ван дер Краан
6
yield return new ValidationResult («Заголовок является обязательным.», «Заголовок»); добавит имя свойства, полезное при группировке ошибок проверки для отображения при необходимости.
Майк Кингскотт,
5
Обратите внимание, что этот метод проверки вызывается только после того, как все атрибуты проверки прошли проверку.
Педро
3
Это сработало для меня, поскольку моя проверка была очень конкретной. Добавление настраиваемого атрибута было бы для меня излишним, поскольку проверка не собиралась повторно использовать.
Steve S
Это то, что я ищу. Спасибо!
Амол Джадхав
27

ExpressiveAnnotations дает вам такую ​​возможность:

[Required]
[AssertThat("Length(FieldA) + Length(FieldB) + Length(FieldC) + Length(FieldD) > 50")]
public string FieldA { get; set; }
Jwaliszko
источник
Это великолепно! мои молитвы получили ответ :)
Korayem
Только что нашел этот ответ, и это просто сэкономило массу времени. Выразительные аннотации великолепны!
Брэд,
10

Чтобы улучшить ответ Дарина, он может быть немного короче:

public class UniqueFileName : ValidationAttribute
{
    private readonly NewsService _newsService = new NewsService();

    public override bool IsValid(object value)
    {
        if (value == null) { return false; }

        var file = (HttpPostedFile) value;

        return _newsService.IsFileNameUnique(file.FileName);
    }
}

Модель:

[UniqueFileName(ErrorMessage = "This file name is not unique.")]

Обратите внимание, что требуется сообщение об ошибке, иначе сообщение об ошибке будет пустым.

Джейми
источник
8

Задний план:

Проверка модели необходима для гарантии того, что полученные данные действительны и верны, чтобы мы могли выполнять дальнейшую обработку с этими данными. Мы можем проверить модель в методе действия. Встроенные атрибуты проверки: Compare, Range, RegularExpression, Required, StringLength. Однако у нас могут быть сценарии, в которых нам потребуются атрибуты проверки, отличные от встроенных.

Настраиваемые атрибуты проверки

public class EmployeeModel 
{
    [Required]
    [UniqueEmailAddress]
    public string EmailAddress {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}
    public int OrganizationId {get;set;}
}

Чтобы создать настраиваемый атрибут проверки, вам необходимо унаследовать этот класс от ValidationAttribute.

public class UniqueEmailAddress : ValidationAttribute
{
    private IEmployeeRepository _employeeRepository;
    [Inject]
    public IEmployeeRepository EmployeeRepository
    {
        get { return _employeeRepository; }
        set
        {
            _employeeRepository = value;
        }
    }
    protected override ValidationResult IsValid(object value,
                        ValidationContext validationContext)
    {
        var model = (EmployeeModel)validationContext.ObjectInstance;
        if(model.Field1 == null){
            return new ValidationResult("Field1 is null");
        }
        if(model.Field2 == null){
            return new ValidationResult("Field2 is null");
        }
        if(model.Field3 == null){
            return new ValidationResult("Field3 is null");
        }
        return ValidationResult.Success;
    }
}

Надеюсь это поможет. Ура!

Ссылки

Ясир Шейх
источник
1

Немного поздно ответить, но кто ищет. Вы можете легко сделать это, используя дополнительное свойство с аннотацией данных:

public string foo { get; set; }
public string bar { get; set; }

[MinLength(20, ErrorMessage = "too short")]
public string foobar 
{ 
    get
    {
        return foo + bar;
    }
}

Это все, что есть на самом деле. Если вы действительно хотите отобразить в определенном месте и ошибку проверки, вы можете добавить это в свое представление:

@Html.ValidationMessage("foobar", "your combined text is too short")

выполнение этого в представлении может пригодиться, если вы хотите выполнить локализацию.

Надеюсь это поможет!

Лео Мюллер
источник