Vraag Vereist indien voorwaardelijke validatie kenmerk


Ik was op zoek naar advies over de beste manier om een ​​validatieattribuut te implementeren dat het volgende doet.

Model

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

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

}

Ik wil tenminste prop1 prop2 prop3 hebben met een waarde en als prop3 de enige waarde is die gevuld is, mag deze niet gelijk zijn aan false. Hoe zou ik hiervoor een validatiekenmerk (s?) Moeten schrijven?

Bedankt voor alle hulp!


45
2017-09-12 16:18


oorsprong


antwoorden:


U kunt de blogbericht volgen voor een voorbeeldimplementatie van een [RequiredIf] aangepast validatiekenmerken. Het vergelijkt met een andere eigenschapwaarde, maar u kunt het eenvoudig aanpassen IsValid methode om aan uw vereisten te voldoen.


21
2017-09-12 16:20



Ik had gisteren hetzelfde probleem, maar ik deed het op een zeer schone manier, wat zowel voor client- als voor server-side validatie werkt.

Staat: Op basis van de waarde van andere eigenschappen in het model, wilt u een andere eigenschap verplicht stellen. Hier is de code

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 is het eigendom waarop u uw toestand wilt vestigen
DesiredValue is de specifieke waarde van de PropertyName (property) waarvoor uw andere property moet worden gevalideerd voor vereist

Stel dat je het volgende hebt:

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

Eindelijk, maar niet op de laatste plaats, registreer adapter voor uw attribuut, zodat het client-side validatie kan doen (ik zet het in global.asax, Application_Start)

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

EDITED

Sommige mensen klagen dat de client side flitst ongeacht wat of het niet werkt. Dus heb ik de bovenstaande code aangepast om ook voorwaardelijke client side validatie met javascript te doen. In dit geval hoeft u de adapter niet te registreren

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

En ten slotte het javascript (bundel het en renderit ... zet het in zijn eigen scriptbestand)

$.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;
});

U moet natuurlijk de niet-storende valideren jQuery als vereiste opnemen


76
2018-04-12 15:57



Ik weet dat het onderwerp al een tijdje geleden werd gesteld, maar onlangs had ik te maken gehad met een soortgelijk probleem en vond ik weer een andere, maar naar mijn mening een completere oplossing. Ik heb besloten om een ​​mechanisme te implementeren dat voorwaardelijke attributen biedt om validatieresultaten te berekenen op basis van andere eigenschappen en relaties tussen deze waarden, die worden gedefinieerd in logische expressies.

Hiermee kunt u het gewenste resultaat op de volgende manier bereiken:

[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; }

Meer informatie over bibliotheek ExpressiveAnnotations is hier te vinden. Het zou veel declaratieve validatiezaken moeten vereenvoudigen zonder de noodzaak om extra case-specifieke attributen te schrijven of een imperatieve manier van validatie in controllers te gebruiken.


26
2017-08-14 15:27



Ik heb het laten werken op ASP.NET MVC 5

Ik zag dat veel mensen geïnteresseerd waren in en lijden aan deze code en ik weet dat het voor het eerst echt verwarrend en verstorend is.

Notes

  • gewerkt aan MVC 5 op zowel server- als clientzijde: D
  • Ik heb de bibliotheek "ExpressiveAnnotations" helemaal niet geïnstalleerd.
  • Ik neem de originele code over van "@Dan Hunex", Vind hem hierboven

Tips om deze fout te verhelpen

"Het type System.Web.Mvc.RequiredAttributeAdapter moet een openbare constructor hebben die drie parameters van de typen System.Web.Mvc.ModelMetadata, System.Web.Mvc.ControllerContext en ExpressiveAnnotations.Attributes accepteert.RequiredIfAttribute Parameternaam: adapterType"

Tip # 1: zorg ervoor dat je er van erven 'ValidationAttribute' niet van 'RequiredAttribute'

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

Tip # 2: OF verwijder deze hele regel uit 'global.asax', Het is helemaal niet nodig in de nieuwere versie van de code (na bewerking door @Dan_Hunex), en ja, deze regel was een must in de oude versie ...

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

Tips om Javascript Code Part Work te krijgen

1- plaats de code in een nieuw js-bestand (bijv .: requiredIfValidator.js)

2- warp de code in een $ (document) .ready (function () {........});

3- include ons js-bestand na het opnemen van de JQuery-validatiebibliotheken, dus het ziet er nu als volgt uit:

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

4- Bewerk de C # -code

van

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

naar

rule.ValidationParameters["dependentproperty"] = PropertyName;

en van

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

naar

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

Mijn volledige code is up and running

global.asax

Hier niets toe te voegen, houd het schoon

requiredIfValidator.js

maak dit bestand aan in ~ / content of in ~ / scripts map

    $.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 of de weergave

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

RequiredIfAttribute.cs Class

maak er een paar waar in je project, bijvoorbeeld in ~ / 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;
        }
    }
}

Het model 

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

Het uitzicht

Niets om hier op te merken eigenlijk ...

    @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>
}

Ik kan hier later een voorbeeld van een project voor uploaden ...

Ik hoop dat dit nuttig was

Dank je


5
2018-05-01 14:36



Uitgaand van de aantekeningen van Adel Mourad en Dan Hunex, heb ik de code aangepast om een ​​voorbeeld te geven dat alleen die waarden accepteert Niet doen overeenkomen met de opgegeven waarde.

Ik ontdekte ook dat ik JavaScript niet nodig had.

Ik heb de volgende klasse toegevoegd aan mijn map Modellen:

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

Ik hoefde mijn mening niet te wijzigen, maar heeft wel de eigenschappen van mijn model gewijzigd:

    [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; }

Ik hoop dat dit helpt!


2
2017-10-31 09:47



Als u "ModelState.Remove" of "ModelState [" Prop "] probeert te gebruiken. Errors.Clear ()" de "ModelState.IsValid" retourneert stil.

Waarom niet alleen de standaard "Vereiste" annotatie van het model verwijderen en uw aangepaste validatie maken vóór de "ModelState.IsValid" op de actie 'Bericht' van de controller? Soortgelijk:

if (!String.IsNullOrEmpty(yourClass.Property1) && String.IsNullOrEmpty(yourClass.dependantProperty))            
            ModelState.AddModelError("dependantProperty", "It´s necessary to select some 'dependant'.");

0
2017-08-25 12:39



Het belangrijkste verschil met andere oplossingen hier is dat deze logica opnieuw gebruikt RequiredAttribute aan de serverkant, en gebruikt requiredde validatiemethode depends eigendom aan de kant van de klant:

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
2018-01-09 23:10