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

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

Например, скажем, у нас есть следующая модель (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

Я решил это, обратившись к "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.

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

Ответ 2

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

Ответ 3

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

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

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));

Ответ 4

Я использую этот удивительный 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; }

Ответ 5

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

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

Ответ 7

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

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

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

Ответ 8

Вам необходимо пройти проверку на уровне Лица, а не на старшем уровне, или Старший должен иметь ссылку на своего родителя. Мне кажется, что вам нужен механизм проверки подлинности, который определяет валидацию для Лица, а не одного из его свойств. Я не уверен, но я не думаю, что 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));
        }
    }
}

Ответ 10

У меня была такая же проблема, мне нужна модификация атрибута [Обязательный] - введите поле в зависимости от 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; }

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

Ответ 11

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

  • Сделать условную первую часть действия контроллера
  • Выполните логику для удаления ошибки из 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 с данными, заполненными ими из пользовательского интерфейса.

Ответ 12

Я использую 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". Затем вам просто нужно проверить валидацию в своем посту.