Je cherchais des conseils sur la meilleure façon de mettre en œuvre un attribut de validation qui fait ce qui suit.
Modèle
public class MyInputModel
{
[Required]
public int Id {get;set;}
public string MyProperty1 {get;set;}
public string MyProperty2 {get;set;}
public bool MyProperty3 {get;set;}
}
Je veux avoir au moins prop1 prop2 prop3 avec une valeur et si prop3 est la seule valeur remplie, elle ne devrait pas être fausse. Comment pourrais-je écrire un ou plusieurs attributs de validation pour cela?
Merci pour toute aide!
J'ai eu le même problème hier, mais je l'ai fait d'une manière très propre qui fonctionne à la fois pour la validation côté client et côté serveur.
Condition: En fonction de la valeur d'une autre propriété dans le modèle, vous souhaitez rendre une autre propriété requise. Voici le 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
est la propriété sur laquelle vous souhaitez effectuer votre conditionDesiredValue
est la valeur particulière du PropertyName (propriété) pour laquelle votre autre propriété doit être validée
Disons que vous disposez des éléments suivants:
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; }
}
Enfin, mais non le moindre, enregistrez l'adaptateur pour votre attribut afin qu'il puisse faire une validation côté client (je l'ai mis dans global.asax, Application_Start)
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute),
typeof(RequiredAttributeAdapter));
ÉDITÉ
Certaines personnes se plaignaient que le côté client tire quoi qu'il arrive ou que cela ne fonctionne pas. J'ai donc modifié le code ci-dessus pour effectuer également une validation conditionnelle côté client avec javascript. Dans ce cas, vous n'avez pas besoin d'enregistrer l'adaptateur
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;
}
}
Et enfin le javascript (le regrouper et le rendre ... le mettre dans son propre fichier script)
$.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;
});
Vous avez évidemment besoin que la requête de validation discrète soit incluse comme exigence
Je sais que le sujet a été posé il y a quelque temps, mais récemment, j'avais rencontré un problème similaire et en ai trouvé un autre, mais à mon avis, une solution plus complète. J'ai décidé de mettre en œuvre un mécanisme qui fournit des attributs conditionnels pour calculer les résultats de validation en fonction d'autres valeurs de propriétés et des relations entre elles, qui sont définies dans des expressions logiques.
En l'utilisant, vous pouvez obtenir le résultat que vous avez demandé de la manière suivante:
[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; }
Plus d'informations sur la bibliothèque ExpressiveAnnotations peuvent être trouvées ici . Il devrait simplifier de nombreux cas de validation déclarative sans qu'il soit nécessaire d'écrire des attributs spécifiques au cas supplémentaires ou d'utiliser une méthode impérative de validation à l'intérieur des contrôleurs.
J'ai vu beaucoup de gens intéressés et souffrant de ce code et je sais que c'est vraiment déroutant et perturbant pour la première fois.
"Le type System.Web.Mvc.RequiredAttributeAdapter doit avoir un constructeur public qui accepte trois paramètres de types System.Web.Mvc.ModelMetadata, System.Web.Mvc.ControllerContext et ExpressiveAnnotations.Attributes.RequiredIfAttribute Nom du paramètre: adapterType"
Astuce # 1: assurez-vous que vous héritez de 'ValidationAttribute' pas de 'RequiredAttribute'
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable { ...}
Astuce n ° 2: OR supprimez cette ligne entière de 'Global.asax', ce n'est pas du tout nécessaire dans la nouvelle version de le code (après modification par @Dan_Hunex), et oui cette ligne était un must dans l'ancienne version ...
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(RequiredIfAttribute), typeof(RequiredAttributeAdapter));
1- mettre le code dans un nouveau fichier js (ex: requiredIfValidator.js)
2- déforme le code dans un $ (document) .ready (function () {........});
3- inclure notre fichier js après avoir inclus les bibliothèques de validation JQuery, donc ça ressemble à ceci maintenant:
@Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>
4- Modifier le code C #
de
rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(PropertyName);
à
rule.ValidationParameters["dependentproperty"] = PropertyName;
et de
if (dependentValue.ToString() == DesiredValue.ToString())
à
if (dependentValue != null && dependentValue.ToString() == DesiredValue.ToString())
Global.asax
Rien à ajouter ici, gardez-le propre
requiredIfValidator.js
créer ce fichier dans ~/content ou dans le dossier ~/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 ou la vue
@Scripts.Render("~/bundles/jqueryval")
<script src="~/Content/JS/requiredIfValidator.js"></script>
classe RequiredIfAttribute.cs
créez-le quelque part dans votre projet, par exemple dans ~/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;
}
}
}
Le modèle
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; }
}
}
La vue
Rien à noter ici en fait ...
@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>
}
Je peux télécharger un exemple de projet pour cela plus tard ...
J'espère que cela vous a été utile
Merci
En développant les notes d'Adel Mourad et de Dan Hunex, j'ai modifié le code pour fournir un exemple qui n'accepte que des valeurs qui ne pas correspondent à la valeur donnée.
J'ai également constaté que je n'avais pas besoin du JavaScript.
J'ai ajouté la classe suivante à mon dossier Modèles:
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;
}
Je n'ai pas eu besoin de modifier ma vue, mais j'ai modifié les propriétés de mon modèle:
[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; }
J'espère que cela t'aides!
Si vous essayez d'utiliser "ModelState.Remove" ou "ModelState [" Prop "]. Errors.Clear ()" le stil "ModelState.IsValid" renvoie false.
Pourquoi ne pas simplement supprimer l'annotation "Obligatoire" par défaut du modèle et effectuer votre validation personnalisée avant l'action "ModelState.IsValid" sur l'action "Post" du contrôleur? Comme ça:
if (!String.IsNullOrEmpty(yourClass.Property1) && String.IsNullOrEmpty(yourClass.dependantProperty))
ModelState.AddModelError("dependantProperty", "It´s necessary to select some 'dependant'.");
La principale différence avec les autres solutions ici est que celle-ci réutilise la logique dans RequiredAttribute
côté serveur et utilise la méthode de validation de required
depends
propriété côté client:
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;
});