RequeridoSi Atributo de Validación Condicional

Resuelto zSynopsis asked hace 13 años • 0 respuestas

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 completado, no debería ser falso. ¿Cómo haría para escribir atributos de validación para esto?

¡Gracias por cualquier ayuda!

zSynopsis avatar Sep 12 '11 23:09 zSynopsis
Aceptado

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 : Según el valor de otra propiedad en el modelo, desea 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;
  }
}

PropertyNamees la propiedad sobre la cual desea establecer su condición
DesiredValuees el valor particular del Nombre de propiedad (propiedad) para el cual su otra propiedad debe validarse según sea necesario

Digamos que tiene 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, registre el adaptador para su atributo para que pueda realizar la validación del lado del cliente (lo puse en global.asax, Application_Start)

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

EDITADO

Algunas personas han informado problemas que el lado del cliente activa sin importar qué o no funciona. Así que modifiqué el código anterior para realizar también una validación condicional del lado del cliente con Javascript. Para este caso no es necesario 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<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;
        }
    }

Y finalmente el javascript (agruparlo y renderizarlo... ponerlo 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;
});

Obviamente necesita que jQuery de validación discreta se incluya como requisito

Dan Hunex avatar Apr 12 '2013 15:04 Dan Hunex

Sé que el tema se planteó 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 los resultados de la validación en función de los valores de otras propiedades y las relaciones entre ellos, que se definen en expresiones lógicas.

Al usarlo, podrá 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 del caso o utilizar una forma imperativa de validación dentro de los controladores.

jwaliszko avatar Aug 14 '2013 15:08 jwaliszko