it-roy-ru.com

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

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

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

118
Peter Stegnar

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

Вот решение:

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

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

public bool PropertyThatRequiredAnotherFieldToBeFilled
{
  get;
  set;
}

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

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

... в каком-то действии контроллера ...

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

...

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


Обновление:

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

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

61
Peter Stegnar

есть гораздо лучший способ добавить правила условной проверки в 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/introduction-asp-net-mvc-3-preview-1.aspx

141
viperguynaz

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

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

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));
33
Dan Hunex

Я использовал этот удивительный нюгет, который делает динамические аннотации 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; }
27
Korayem

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

ModelState["DependentProperty"].Errors.Clear();
17
Pavel Chuchuva

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

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

Саймон

8
Simon Ince

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

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

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

6
bojingo

У меня была та же проблема, мне нужно было изменить атрибут [Required] - сделать поле обязательным в зависимости от 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
Den

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

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

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

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

2
Merritt

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

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

Пример:

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

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

0
Jeremy Ray Brown

Я использую 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
fosterImposter