ASP.NET MVC Условная проверка

111

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

Например, скажем, у нас есть следующая модель (Person and Senior):

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 → required). Как реализовать условную проверку в ASP.NET MVC 2 с аннотациями данных?

  • 1
    Я недавно задавал похожий вопрос: stackoverflow.com/questions/2280539/…
  • 0
    Я не совсем понимаю. Senior объект всегда старший, так почему IsSenior может быть ложным в этом случае. Разве вам не нужно, чтобы свойство Person.Senior было нулевым, если Person.IsSenior имеет значение false. Или почему бы не реализовать свойство IsSenior следующим образом: bool IsSenior { get { return this.Senior != null; } } .
Показать ещё 2 комментария
Теги:
validation
forms
asp.net-mvc

12 ответов

56
Лучший ответ

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

Вот решение:

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

... В некотором классе...

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

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

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

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

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

...

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


UPDATE:

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

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

  • 16
    Недостатком является то, что одна часть вашей логики проверки находится в модели, а другая часть в контроллере (ах).
  • 0
    Ну, конечно, это не обязательно. Я просто показываю самый простой пример. Я реализовал это с интерфейсом на модели и с атрибутом действия, который проверяет модель, которая реализует упомянутый интерфейс. Интерфейс вспотевает метод Validate (ModelStateDictionary modelState). Итак, наконец, вы делаете все проверки в модели. Во всяком случае, хорошая мысль.
Показать ещё 7 комментариев
122

есть намного лучший способ добавить правила условной валидации в 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

  • 2
    +1, реализация IValidateObject - это круто! :) Тем не менее, ваши скобки были едва сняты (я отредактировал их, чтобы исправить это).
  • 0
    если свойство имеет тип int, для которого требуется значение, если заполнить это поле, проверка не работает.
Показать ещё 12 комментариев
30

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

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

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));
  • 0
    Это была исходная отправная точка miroprocessordev.blogspot.com/2012/08/…
  • 0
    Есть ли какое-нибудь эквивалентное решение в asp.net mvc2? Классы ValidationResult, ValidationContext недоступны в asp.net mvc2 (.net framework 3.5)
Показать ещё 6 комментариев
24

Я использую этот удивительный nuget, который делает динамические аннотации 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; }
  • 3
    Библиотека ExpressiveAnnotation является наиболее гибким и универсальным решением всех ответов здесь. Спасибо, что поделился!
  • 2
    Я бился головой, пытаясь найти решение для хорошего дня. Выразительные аннотации, кажется, исправят меня!
Показать ещё 3 комментария
16

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

ModelState["DependentProperty"].Errors.Clear();
9

Спасибо Merritt:)

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

Саймон

6

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

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

Он доступен как пакет NuGet.

3

У меня была такая же проблема, мне нужна модификация атрибута [Обязательный] - введите поле в зависимости от http-запроса. Решение было похоже на ответ Дэна Хунэса, но его решение не сработало правильно (см. комментарии). Я не использую ненавязчивую проверку, просто 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; }

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

  • 0
    Часть о расширении DataAnnotationsModelValidator была именно тем, что мне нужно было увидеть. Спасибо.
3

Посмотрите на этого парня:

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

Сейчас я работаю над своим примером.

3

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

Я знаю, что блок Application Validation поддерживает самооценку из коробки, но 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));
        }
    }
}
  • 0
    «Вам необходимо выполнить проверку на уровне Person, а не на уровне Senior». Да, это вариант, но вы теряете возможность добавления ошибки к определенному полю, что требуется для объекта Senior.
0

Я использую MVC 5, но вы можете попробовать что-то вроде этого:

public DateTime JobStart { get; set; }

[AssertThat("StartDate >= JobStart", ErrorMessage = "Time Manager may not begin before job start date")]
[DisplayName("Start Date")]
[Required]
public DateTime? StartDate { get; set; }

В вашем случае вы скажете что-то вроде "IsSenior == true". Затем вам просто нужно проверить валидацию в своем посту.

0

Типичное использование для условного удаления ошибки из состояния модели:

  • Сделать условную первую часть действия контроллера
  • Выполните логику для удаления ошибки из ModelState
  • Остальная часть существующей логики (обычно проверка состояния модели, затем все остальное)

Пример:

public ActionResult MyAction(MyViewModel vm)
{
    // perform conditional test
    // if true, then remove from ModelState (e.g. ModelState.Remove("MyKey")

    // Do typical model state validation, inside following if:
    //     if (!ModelState.IsValid)

    // Do rest of logic (e.g. fetching, saving

В вашем примере сохраните все как есть и добавьте логику, предложенную вашему Action Controller. Я предполагаю, что ваш ViewModel, переданный в действие контроллера, имеет объекты Person и Senior Person с данными, заполненными ими из пользовательского интерфейса.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню