RequiredIf Условный атрибут проверки - PullRequest
50 голосов
/ 12 сентября 2011

Я искал несколько советов о том, как лучше всего реализовать атрибут валидации, который выполняет следующие действия.

Модель

public class MyInputModel 
{
    [Required]
    public int Id {get;set;}

    public string MyProperty1 {get;set;}
    public string MyProperty2 {get;set;}
    public bool MyProperty3 {get;set;}

}

Я хочу, чтобы по крайней мере prop1 prop2 prop3 со значением, и если prop3 - единственное значение, заполненное им, оно не должно равняться false. Как мне написать для этого атрибут (ы) проверки?

Спасибо за любую помощь!

Ответы [ 6 ]

85 голосов
/ 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 enum UserType
{
  Admin,
  Regular
}

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

EDITED

Некоторые люди жаловались на то, что клиентская сторона стреляет независимо от того, что или она не работает. Поэтому я изменил приведенный выше код для выполнения условной проверки на стороне клиента также с помощью JavaScript. Для этого случая вам не нужно регистрировать адаптер

 public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
    {
        private String PropertyName { get; set; }
        private Object DesiredValue { get; set; }
        private readonly RequiredAttribute _innerAttribute;

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

        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);

            if (dependentValue.ToString() == DesiredValue.ToString())
            {
                if (!_innerAttribute.IsValid(value))
                {
                    return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
                }
            }
            return ValidationResult.Success;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = ErrorMessageString,
                ValidationType = "requiredif",
            };
            rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
            rule.ValidationParameters["desiredvalue"] = DesiredValue is bool ? DesiredValue.ToString().ToLower() : DesiredValue;

            yield return rule;
        }
    }

И, наконец, javascript (связать его и визуализировать ... поместить в отдельный файл скрипта)

$.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'desiredvalue'], function (options) {
    options.rules['requiredif'] = options.params;
    options.messages['requiredif'] = options.message;
});

$.validator.addMethod('requiredif', function (value, element, parameters) {
    var desiredvalue = parameters.desiredvalue;
    desiredvalue = (desiredvalue == null ? '' : desiredvalue).toString();
    var controlType = $("input[id$='" + parameters.dependentproperty + "']").attr("type");
    var actualvalue = {}
    if (controlType == "checkbox" || controlType == "radio") {
        var control = $("input[id$='" + parameters.dependentproperty + "']:checked");
        actualvalue = control.val();
    } else {
        actualvalue = $("#" + parameters.dependentproperty).val();
    }
    if ($.trim(desiredvalue).toLowerCase() === $.trim(actualvalue).toLocaleLowerCase()) {
        var isValid = $.validator.methods.required.call(this, value, element, parameters);
        return isValid;
    }
    return true;
});

Вам нужно, чтобы ненавязчивый валидатор JQuery был включен в качестве требования

30 голосов
/ 14 августа 2013

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

Используя его, вы можете достичь запрошенного результата следующим образом:

[RequiredIf("MyProperty2 == null && MyProperty3 == false")]
public string MyProperty1 { get; set; }

[RequiredIf("MyProperty1 == null && MyProperty3 == false")]
public string MyProperty2 { get; set; }

[AssertThat("MyProperty1 != null || MyProperty2 != null || MyProperty3 == true")]
public bool MyProperty3 { get; set; }

Более подробную информацию о библиотеке ExpressiveAnnotations можно найти здесь .Это должно упростить многие случаи декларативной проверки без необходимости написания дополнительных атрибутов для конкретного случая или использования обязательного способа проверки внутри контроллеров.

6 голосов
/ 01 мая 2017

Я получил его для работы на ASP.NET MVC 5

Я видел много людей, интересующихся и страдающих от этого кода, и я знаю, что он действительно запутывает и разрушает впервые.

Примечания

  • работал на MVC 5 как на стороне сервера, так и на стороне клиента: D
  • Я вообще не устанавливал библиотеку "ExpressiveAnnotations".
  • Я беру исходный код из " @ Dan Hunex ", найди его выше

Советы по исправлению этой ошибки

"Тип System.Web.Mvc.RequiredAttributeAdapter должен иметь открытый конструктор, который принимает три параметра типов System.Web.Mvc.ModelMetadata, System.Web.Mvc.ControllerContext и ExpressiveAnnotations.Attributes.RequiredIfAttribuT name: «

Совет № 1: убедитесь, что вы наследуете от ' ValidationAttribute ' не от ' RequiredAttribute '

 public class RequiredIfAttribute : ValidationAttribute, IClientValidatable { ...}

Совет № 2: ИЛИ удалите всю эту строку из ' Global.asax ', в новой версии кода она вообще не нужна (после редактирования @Dan_Hunex ) и да, эта строка была обязательной в старой версии ...

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

Советы по работе над частью кода Javascript

1 - поместить код в новый файл js (например: requiredIfValidator.js)

2 - деформировать код внутри $ (документа) .ready (function () {........});

3 - включите наш js-файл после включения библиотек проверки JQuery, поэтому теперь это выглядит так:

@Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>

4- Изменить код C #

от

rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);

до

rule.ValidationParameters["dependentproperty"] = PropertyName;

и от

if (dependentValue.ToString() == DesiredValue.ToString())

до

if (dependentValue != null && dependentValue.ToString() == DesiredValue.ToString())

Мой весь код запущен и работает

Global.asax

Здесь нечего добавить, держите его в чистоте

requiredIfValidator.js

создайте этот файл в ~ / content или в папке ~ / scripts

    $.validator.unobtrusive.adapters.add('requiredif', ['dependentproperty', 'desiredvalue'], function (options)
{
    options.rules['requiredif'] = options.params;
    options.messages['requiredif'] = options.message;
});


$(document).ready(function ()
{

    $.validator.addMethod('requiredif', function (value, element, parameters) {
        var desiredvalue = parameters.desiredvalue;
        desiredvalue = (desiredvalue == null ? '' : desiredvalue).toString();
        var controlType = $("input[id$='" + parameters.dependentproperty + "']").attr("type");
        var actualvalue = {}
        if (controlType == "checkbox" || controlType == "radio") {
            var control = $("input[id$='" + parameters.dependentproperty + "']:checked");
            actualvalue = control.val();
        } else {
            actualvalue = $("#" + parameters.dependentproperty).val();
        }
        if ($.trim(desiredvalue).toLowerCase() === $.trim(actualvalue).toLocaleLowerCase()) {
            var isValid = $.validator.methods.required.call(this, value, element, parameters);
            return isValid;
        }
        return true;
    });
});

_Layout.cshtml или View

@Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>

RequiredIfAttribute.cs Class

создайте его где-нибудь в вашем проекте, например в ~ / models / customValidation /

    using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace Your_Project_Name.Models.CustomValidation
{
    public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
    {
        private String PropertyName { get; set; }
        private Object DesiredValue { get; set; }
        private readonly RequiredAttribute _innerAttribute;

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

        protected override ValidationResult IsValid(object value, ValidationContext context)
        {
            var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);

            if (dependentValue != null && dependentValue.ToString() == DesiredValue.ToString())
            {
                if (!_innerAttribute.IsValid(value))
                {
                    return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
                }
            }
            return ValidationResult.Success;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = ErrorMessageString,
                ValidationType = "requiredif",
            };
            rule.ValidationParameters["dependentproperty"] = PropertyName;
            rule.ValidationParameters["desiredvalue"] = DesiredValue is bool ? DesiredValue.ToString().ToLower() : DesiredValue;

            yield return rule;
        }
    }
}

Модель

    using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using Your_Project_Name.Models.CustomValidation;

namespace Your_Project_Name.Models.ViewModels
{

    public class CreateOpenActivity
    {
        public Nullable<int> ORG_BY_CD { get; set; }

        [RequiredIf("ORG_BY_CD", "5", ErrorMessage = "Coordinator ID is required")] // This means: IF 'ORG_BY_CD' is equal 5 (for the example)  > make 'COR_CI_ID_NUM' required and apply its all validation / data annotations
        [RegularExpression("[0-9]+", ErrorMessage = "Enter Numbers Only")]
        [MaxLength(9, ErrorMessage = "Enter a valid ID Number")]
        [MinLength(9, ErrorMessage = "Enter a valid ID Number")]
        public string COR_CI_ID_NUM { get; set; }
    }
}

Вид

Здесь на самом деле нечего заметить ...

    @model Your_Project_Name.Models.ViewModels.CreateOpenActivity
@{
    ViewBag.Title = "Testing";
}

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>CreateOpenActivity</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

        <div class="form-group">
            @Html.LabelFor(model => model.ORG_BY_CD, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ORG_BY_CD, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.ORG_BY_CD, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.COR_CI_ID_NUM, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.COR_CI_ID_NUM, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.COR_CI_ID_NUM, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

Я могу загрузить образец проекта для этого позже ...

Надеюсь, это было полезно

Спасибо

2 голосов
/ 31 октября 2017

Расширяя примечания Аделя Мурада и Дана Хунекса, я исправил код, чтобы предоставить пример, который принимает только значения, которые не соответствуют данному значению.

Я также обнаружил, что мне не нужен JavaScript.

Я добавил следующий класс в папку «Мои модели»:

public class RequiredIfNotAttribute : ValidationAttribute, IClientValidatable
{
    private String PropertyName { get; set; }
    private Object InvalidValue { get; set; }
    private readonly RequiredAttribute _innerAttribute;

    public RequiredIfNotAttribute(String propertyName, Object invalidValue)
    {
        PropertyName = propertyName;
        InvalidValue = invalidValue;
        _innerAttribute = new RequiredAttribute();
    }

    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        var dependentValue = context.ObjectInstance.GetType().GetProperty(PropertyName).GetValue(context.ObjectInstance, null);

        if (dependentValue.ToString() != InvalidValue.ToString())
        {
            if (!_innerAttribute.IsValid(value))
            {
                return new ValidationResult(FormatErrorMessage(context.DisplayName), new[] { context.MemberName });
            }
        }
        return ValidationResult.Success;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = ErrorMessageString,
            ValidationType = "requiredifnot",
        };
        rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
        rule.ValidationParameters["invalidvalue"] = InvalidValue is bool ? InvalidValue.ToString().ToLower() : InvalidValue;

        yield return rule;
    }

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

    [RequiredIfNot("Id", 0, ErrorMessage = "Please select a Source")]
    public string TemplateGTSource { get; set; }

    public string TemplateGTMedium
    {
        get
        {
            return "Email";
        }
    }

    [RequiredIfNot("Id", 0, ErrorMessage = "Please enter a Campaign")]
    public string TemplateGTCampaign { get; set; }

    [RequiredIfNot("Id", 0, ErrorMessage = "Please enter a Term")]
    public string TemplateGTTerm { get; set; }

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

0 голосов
/ 10 января 2017

Основным отличием от других решений здесь является то, что это повторно использует логику в RequiredAttribute на стороне сервера и использует depends метод проверки *1003* свойство на стороне клиента:

public class RequiredIf : RequiredAttribute, IClientValidatable
{
    public string OtherProperty { get; private set; }
    public object OtherPropertyValue { get; private set; }

    public RequiredIf(string otherProperty, object otherPropertyValue)
    {
        OtherProperty = otherProperty;
        OtherPropertyValue = otherPropertyValue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        PropertyInfo otherPropertyInfo = validationContext.ObjectType.GetProperty(OtherProperty);
        if (otherPropertyInfo == null)
        {
            return new ValidationResult($"Unknown property {OtherProperty}");
        }

        object otherValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
        if (Equals(OtherPropertyValue, otherValue)) // if other property has the configured value
            return base.IsValid(value, validationContext);

        return null;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule();
        rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
        rule.ValidationType = "requiredif"; // data-val-requiredif
        rule.ValidationParameters.Add("other", OtherProperty); // data-val-requiredif-other
        rule.ValidationParameters.Add("otherval", OtherPropertyValue); // data-val-requiredif-otherval

        yield return rule;
    }
}

$.validator.unobtrusive.adapters.add("requiredif", ["other", "otherval"], function (options) {
    var value = {
        depends: function () {
            var element = $(options.form).find(":input[name='" + options.params.other + "']")[0];
            return element && $(element).val() == options.params.otherval;
        }
    }
    options.rules["required"] = value;
    options.messages["required"] = options.message;
});
0 голосов
/ 25 августа 2016

Если вы попытаетесь использовать "ModelState.Remove" или "ModelState [" Prop "]. Errors.Clear ()", стиль "ModelState.IsValid" возвращает false.

Почему бы просто не удалить аннотацию «Требуется» по умолчанию из модели и выполнить пользовательскую проверку перед действием «ModelState.IsValid» на контроллере «Post»? Как это:

if (!String.IsNullOrEmpty(yourClass.Property1) && String.IsNullOrEmpty(yourClass.dependantProperty))            
            ModelState.AddModelError("dependantProperty", "It´s necessary to select some 'dependant'.");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...