ObligatorioSi el atributo de validación condicional

Estaba buscando algunos consejos sobre la mejor manera de implementar un atributo de validación que haga lo siguiente.

Modelo

public class MyInputModel { [Required] public int Id {get;set;} public string MyProperty1 {get;set;} public string MyProperty2 {get;set;} public bool MyProperty3 {get;set;} } 

Quiero tener al menos prop1 prop2 prop3 con un valor y si prop3 es el único valor que lo llena, no debe ser igual a falso. ¿Cómo voy a escribir un atributo de validación (s?) Para esto?

¡Gracias por cualquier ayuda!

Puede consultar la siguiente publicación de blog para una implementación de muestra de un atributo de validación personalizado [RequiredIf] . Se compara con un valor de propiedad único, pero puede ajustar fácilmente el método IsValid para cumplir sus requisitos.

Tuve el mismo problema ayer, pero lo hice de una manera muy limpia que funciona tanto para la validación del lado del cliente como del lado del servidor.

Condición : en función del valor de otra propiedad en el modelo, desea hacer que se requiera otra propiedad. Aquí está el código

 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 es la propiedad en la que desea realizar su condición
DesiredValue es el valor particular del PropertyName (propiedad) para el que debe validar su otra propiedad para el requisito

Digamos que tienes lo siguiente:

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

Por último, pero no menos importante, registra el adaptador para tu atributo para que pueda hacer la validación del lado del cliente (lo puse en global.asax, Application_Start)

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

EDITADO

Algunas personas se quejaban de que el lado del cliente se dispara sin importar lo que pase o no funciona. Así que modifiqué el código anterior para hacer una validación condicional del lado del cliente con javascript también. Para este caso, no necesita registrar el adaptador

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

Y finalmente el javascript (bundle it y renderit … lo puso en su propio archivo de 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; }); 

Necesitas obviamente la discreta validación de jquery para ser incluida como requisito

Sé que el tema se preguntó hace algún tiempo, pero recientemente me enfrenté a un problema similar y encontré otro, pero en mi opinión una solución más completa. Decidí implementar un mecanismo que proporciona atributos condicionales para calcular resultados de validación basados ​​en otros valores de propiedades y relaciones entre ellos, que se definen en expresiones lógicas.

Utilizándolo, puede lograr el resultado que solicitó de la siguiente manera:

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

Puede encontrar más información sobre la biblioteca ExpressiveAnnotations aquí . Debería simplificar muchos casos de validación declarativa sin la necesidad de escribir atributos adicionales específicos de cada caso o usar una forma imperativa de validación dentro de los controladores.

Lo tengo para trabajar en ASP.NET MVC 5

Vi a mucha gente interesada y sufriendo por este código y sé que es realmente confuso e inquietante por primera vez.

Notas

  • trabajado en MVC 5 tanto en el lado del servidor como del cliente: D
  • No instalé la biblioteca “ExpressiveAnnotations” en absoluto.
  • Estoy tomando el código original de ” @ Dan Hunex “, búscalo arriba

Consejos para arreglar este error

“El tipo System.Web.Mvc.RequiredAttributeAdapter debe tener un constructor público que acepte tres parámetros de tipos System.Web.Mvc.ModelMetadata, System.Web.Mvc.ControllerContext y ExpressiveAnnotations.Attributes.RequiredIfAttribute Nombre del parámetro: adapterType”

Consejo # 1: asegúrese de heredar de ‘ ValidationAttribute ‘ no de ‘ RequiredAttribute

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

Consejo # 2: O elimine esta línea completa de ‘ Global.asax ‘, no es necesario en absoluto en la versión más nueva del código (después de editar por @Dan_Hunex), y sí esta línea era imprescindible en la versión anterior. .

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

Consejos para obtener el código de JavaScript parte de trabajo

1- poner el código en un nuevo archivo js (por ejemplo: requiredIfValidator.js)

2- deformar el código dentro de $ (document) .ready (function () {……..});

3- incluye nuestro archivo js después de incluir las bibliotecas de validación de JQuery, así se ve así ahora:

 @Scripts.Render("~/bundles/jqueryval")  

4- Edita el código C #

de

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

a

 rule.ValidationParameters["dependentproperty"] = PropertyName; 

y de

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

a

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

Mi todo el código está en funcionamiento

Global.asax

Nada para agregar aquí, mantenerlo limpio

requiredIfValidator.js

crea este archivo en ~ / contenido o en la carpeta ~ / 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 o la vista

 @Scripts.Render("~/bundles/jqueryval")  

Clase RequiredIfAttribute.cs

créelo en algún lugar de su proyecto, por ejemplo, en ~ / 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 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; } } } 

El modelo

  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 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 vista

Nada que notar aquí en realidad …

  @model Your_Project_Name.Models.ViewModels.CreateOpenActivity @{ ViewBag.Title = "Testing"; } @using (Html.BeginForm()) { @Html.AntiForgeryToken() 

CreateOpenActivity


@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.ORG_BY_CD, htmlAttributes: new { @class = "control-label col-md-2" })
@Html.EditorFor(model => model.ORG_BY_CD, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.ORG_BY_CD, "", new { @class = "text-danger" })
@Html.LabelFor(model => model.COR_CI_ID_NUM, htmlAttributes: new { @class = "control-label col-md-2" })
@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" })
}

Puedo cargar una muestra de proyecto para esto más tarde …

Espero que esto haya sido útil

Gracias

Ampliando las notas de Adel Mourad y Dan Hunex, modifiqué el código para proporcionar un ejemplo que solo acepta valores que no coinciden con el valor dado.

También descubrí que no necesitaba el JavaScript.

Agregué la siguiente clase a mi carpeta de Modelos:

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

No necesité hacer ningún cambio en mi vista, pero realicé un cambio en las propiedades de mi modelo:

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

¡Espero que esto ayude!

Si intenta utilizar “ModeloEstadio.Eliminar” o “EstadoModelo [” Prop “]. Errors.Clear ()” el ​​estado “ModeloEstadoValido” devuelve falso.

¿Por qué no simplemente eliminar la anotación “requerida” por defecto del modelo y hacer su validación personalizada antes de la acción “Publicar” de ModelState.IsValid? Me gusta esto:

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

La principal diferencia con respecto a otras soluciones aquí es que esta reutiliza la lógica en RequiredAttribute en el lado del servidor, y el uso del método de validación required depends propiedad del lado del cliente:

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