ASP.NET MVC Условная проверка - PullRequest
121 голосов
/ 10 марта 2010

Как использовать аннотации данных для условной проверки модели?

Например, допустим, у нас есть следующая модель (человек и старший):

public class Person
{
    [Required(ErrorMessage = "*")]
    public string Name
    {
        get;
        set;
    }

    public bool IsSenior
    {
        get;
        set;
    }

    public Senior Senior
    {
        get;
        set;
    }
}

public class Senior
{
    [Required(ErrorMessage = "*")]//this should be conditional validation, based on the "IsSenior" value
    public string Description
    {
        get;
        set;
    }
}

И следующий вид:

<%= Html.EditorFor(m => m.Name)%>
<%= Html.ValidationMessageFor(m => m.Name)%>

<%= Html.CheckBoxFor(m => m.IsSenior)%>
<%= Html.ValidationMessageFor(m => m.IsSenior)%>

<%= Html.CheckBoxFor(m => m.Senior.Description)%>
<%= Html.ValidationMessageFor(m => m.Senior.Description)%>

Я бы хотел быть условным обязательным полем свойства Senior.Description, основываясь на выборе свойства IsSenior (true -> обязательный) Как реализовать условную валидацию в ASP.NET MVC 2 с аннотациями данных?

Ответы [ 12 ]

143 голосов
/ 12 марта 2011

есть гораздо лучший способ добавить правила условной проверки в MVC3. Пусть ваша модель наследует IValidatableObject и реализует метод Validate:

public class Person : IValidatableObject
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
        if (IsSenior && string.IsNullOrEmpty(Senior.Description)) 
            yield return new ValidationResult("Description must be supplied.");
    }
}

см. Подробное описание на http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx

61 голосов
/ 11 марта 2010

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

Вот решение:

Если вам нужно реализовать условную проверку на основе некоторого поля (например, если A = true, тогда требуется B), при сохранении сообщений об ошибках на уровне свойств (это не true для пользовательских валидаторов, находящихся на уровне объекта), вы можете достичь этого, обрабатывая «ModelState», просто удаляя из него нежелательные проверки.

... В каком-то классе ...

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

[Required(ErrorMessage = "*")] 
public string DepentedProperty
{
  get;
  set;
}

... класс продолжается ...

... В некоторых действиях контроллера ...

if (!PropertyThatRequiredAnotherFieldToBeFilled)
{
   this.ModelState.Remove("DepentedProperty");
}

...

При этом мы достигаем условной проверки, оставляя все остальное таким же.


UPDATE:

Это моя последняя реализация: я использовал интерфейс модели и атрибут действия, который проверяет модель, которая реализует указанный интерфейс. Интерфейс предписывает метод Validate (ModelStateDictionary modelState). Атрибут для действия просто вызывает Validate (modelState) для IValidatorSomething.

Я не хотел усложнять этот ответ, поэтому я не упомянул окончательные детали реализации (которые, в конце концов, имеют значение в рабочем коде).

33 голосов
/ 12 апреля 2013

Вчера у меня была такая же проблема, но я сделал это очень чисто, что работает как для проверки на стороне клиента, так и на стороне сервера.

Условие: на основе значения другого свойства в модели вы хотите сделать другое свойство обязательным. Вот код

public class RequiredIfAttribute : RequiredAttribute
{
    private String PropertyName { get; set; }
    private Object DesiredValue { get; set; }

    public RequiredIfAttribute(String propertyName, Object desiredvalue)
    {
        PropertyName = propertyName;
        DesiredValue = desiredvalue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        Object instance = context.ObjectInstance;
        Type type = instance.GetType();
        Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
        if (proprtyvalue.ToString() == DesiredValue.ToString())
        {
            ValidationResult result = base.IsValid(value, context);
            return result;
        }
        return ValidationResult.Success;
    }
}

Здесь PropertyName - это свойство, на которое вы хотите оформить ваше условие. DesiredValue - это конкретное значение PropertyName (свойство), для которого ваше другое свойство должно быть проверено на требуемое

Скажем, у вас есть следующее

public class User
{
    public UserType UserType { get; set; }

    [RequiredIf("UserType", UserType.Admin, ErrorMessageResourceName = "PasswordRequired", ErrorMessageResourceType = typeof(ResourceString))]
    public string Password
    {
        get;
        set;
    }
}

Наконец, но не в последнюю очередь, зарегистрируйте адаптер для своего атрибута, чтобы он мог выполнять проверку на стороне клиента (я поместил его в global.asax, Application_Start)

 DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),typeof(RequiredAttributeAdapter));
28 голосов
/ 17 июня 2016

Я использовал этот удивительный нюгет, который делает динамические аннотации ExpressiveAnnotations

Вы можете проверить любую логику, о которой мечтаете:

public string Email { get; set; }
public string Phone { get; set; }
[RequiredIf("Email != null")]
[RequiredIf("Phone != null")]
[AssertThat("AgreeToContact == true")]
public bool? AgreeToContact { get; set; }
17 голосов
/ 14 июля 2010

Можно условно отключить валидаторы, удалив ошибки из ModelState:

ModelState["DependentProperty"].Errors.Clear();
8 голосов
/ 06 февраля 2011

Спасибо, Мерритт :) 1001 *

Я только что обновил это до MVC 3 на случай, если кто-нибудь найдет это полезным; http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx

Simon

6 голосов
/ 04 декабря 2013

В настоящее время существует структура, которая выполняет эту условную проверку (среди прочих удобных проверок аннотаций данных) из коробки: http://foolproof.codeplex.com/

В частности, взгляните на валидатор [RequiredIfTrue ("IsSenior")]. Вы помещаете это непосредственно в свойство, которое хотите проверить, чтобы получить желаемое поведение ошибки проверки, связанной со свойством «Senior».

Доступен в виде пакета NuGet.

3 голосов
/ 06 марта 2014

У меня была та же проблема, мне нужно было изменить атрибут [Обязательный] - сделать поле обязательным в зависимости от http-запроса. Решение было похоже на ответ Dan Hunex, но его решение не работало правильно (см. Комментарии). Я не использую ненавязчивую проверку, просто MicrosoftMvcValidation.js из коробки. Вот. Реализуйте свой пользовательский атрибут:

public class RequiredIfAttribute : RequiredAttribute
{

    public RequiredIfAttribute(/*You can put here pararmeters if You need, as seen in other answers of this topic*/)
    {

    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {

    //You can put your logic here   

        return ValidationResult.Success;//I don't need its server-side so it always valid on server but you can do what you need
    }


}

Затем вам нужно внедрить своего собственного провайдера, чтобы использовать его в качестве адаптера в вашем global.asax

public class RequreIfValidator : DataAnnotationsModelValidator <RequiredIfAttribute>
{

    ControllerContext ccontext;
    public RequreIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
       : base(metadata, context, attribute)
    {
        ccontext = context;// I need only http request
    }

//override it for custom client-side validation 
     public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
     {       
               //here you can customize it as you want
         ModelClientValidationRule rule = new ModelClientValidationRule()
         {
             ErrorMessage = ErrorMessage,
    //and here is what i need on client side - if you want to make field required on client side just make ValidationType "required"    
             ValidationType =(ccontext.HttpContext.Request["extOperation"] == "2") ? "required" : "none";
         };
         return new ModelClientValidationRule[] { rule };
      }
}

И измените ваш global.asax с помощью строки

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequreIfValidator));

и вот оно

[RequiredIf]
public string NomenclatureId { get; set; }

Основным преимуществом для меня является то, что мне не нужно кодировать собственный клиентский валидатор, как в случае ненавязчивой проверки. он работает так же, как [Обязательно], но только в тех случаях, когда вы хотите.

3 голосов
/ 10 марта 2010

Вам необходимо подтвердить на уровне личности, а не на уровне старшего, или у старшего должен быть указатель на его родительское лицо. Мне кажется, что вам нужен механизм самоутверждения, который определяет валидацию для Person, а не для одного из его свойств. Я не уверен, но я не думаю, что DataAnnotations поддерживает это из коробки. Что вы можете сделать, создав свой собственный Attribute, производный от ValidationAttribute, который можно оформить на уровне класса, а затем создайте пользовательский валидатор, который также позволяет запускать эти валидаторы на уровне класса.

Я знаю, что Application Validation Block поддерживает самопроверку прямо из коробки, но у VAB довольно крутая кривая обучения. Тем не менее, вот пример использования VAB:

[HasSelfValidation]
public class Person
{
    public string Name { get; set; }
    public bool IsSenior { get; set; }
    public Senior Senior { get; set; }

    [SelfValidation]
    public void ValidateRange(ValidationResults results)
    {
        if (this.IsSenior && this.Senior != null && 
            string.IsNullOrEmpty(this.Senior.Description))
        {
            results.AddResult(new ValidationResult(
                "A senior description is required", 
                this, "", "", null));
        }
    }
}
2 голосов
/ 26 октября 2010

Проверьте этого парня:

http://blogs.msdn.com/b/simonince/archive/2010/06/04/conditional-validation-in-mvc.aspx

Я сейчас работаю над его примером проекта.

...