6

He creado un atributo de validación CompareLessThan personalizada copiando el ASP.NET MVC 3 CompareAttribute y en lugar de la comprobación de la igualdad, que verifique que una propiedad es menor que otro. Si hay un error en el lado del cliente, el mensaje '{0} debe ser menor que {1}' se muestra al usuario.ASP.NET MVC ValidationAttribute Obtener Display Otros Nombre de propiedad

Mi modelo se configura de la siguiente manera con los atributos de Pantalla que hacen referencia a un archivo de recursos.

[CompareLessThan("AmountAvailable", ErrorMessageResourceName="CompareLessThan", ErrorMessageResourceType = typeof(Resources.ValidationMessages))] 
[Display(Name = "Amount", ResourceType = typeof(Resources.Labels))] 
public decimal Amount { get; set; } 

[Display(Name = "AmountAvailable", ResourceType = typeof(Resources.Labels))] 
public decimal AmountAvailable { get; set; } 

A continuación, el método de validación GetClientValidationRules personalizada es exactamente el mismo que en el CompareAttribute

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 
{    
    yield return new ModelClientValidationLessThanRule(FormatErrorMessage(metadata.DisplayName), FormatPropertyForClientValidation(OtherProperty), this.AllowEquality); 
} 

Aquí estamos generando el mensaje de error que se mostrará al usuario si hay un problema. Puedo obtener el nombre para mostrar del archivo de recursos para la propiedad que está decorada con mi atributo personalizado CompareLessThan, pero mi pregunta es ¿cómo hago para obtener el nombre para mostrar de la propiedad 'otra' con la que estamos comparando? En el método IsValid tenemos una referencia a la validationContext de la que puede generar un objeto PropertyInfo para el 'otro' propiedad y creo que obtener el nombre de visualización. Pero, en GetClientValidationRules no tengo acceso a eso.

Siempre podría pasar otro valor para el nombre para mostrar de la otra propiedad pero esperaba que hubiera una forma de derivarlo, ya que lo estoy especificando con anotaciones de datos.

¿Alguna idea?

Respuesta

5

La respuesta proporcionada por nemesv no funcionaba como la propiedad metadata.Model tiene un valor de 0. Sin embargo, a través de los metadatos que tienen el nombre completo del modelo de lo que es posible crear un nuevo instancia de ese modelo y luego cree un nuevo DataAnnonationsModelMetadataProvider desde esa instancia de creación. Desde allí podemos obtener el nombre para mostrar de la otra propiedad.

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 
{ 
    Type type = Type.GetType(metadata.ContainerType.FullName); 
    var model = Activator.CreateInstance(type); 

    var provider = new DataAnnotationsModelMetadataProvider(); 
    var otherMetaData = provider.GetMetadataForProperty(() => model, type, this.OtherProperty); 

    this.otherPropertyDisplayName = otherMetaData.DisplayName; 

    yield return new ModelClientValidationLessThanRule(FormatErrorMessage(metadata.DisplayName), FormatPropertyForClientValidation(this.OtherProperty), this.AllowEquality); 
} 

Realmente no me gusta esta solución (aunque funciona) ya que parece que debería haber una manera mejor. ¿Alguien más tiene alguna otra idea?

4

yo no lo he probado, pero se puede obtener las propiedades de modelo con la propiedad metadata.Properties

metadata.Properties.Single(p => p.PropertyName == "OtherPropName").DisplayName; 

EDIT: Dado que las propiedades está vacía lo que siempre se puede hacer (aunque es muy elegante). Puedes generar los metadatos para ti.

var provider = new DataAnnotationsModelMetadataProvider(); 
var otherMetaData = provider.GetMetadataForProperty(() => metaData.Model, metaData.ModelType, "OtherPropertyName"); 
+0

Buena idea, pero la colección Propiedades siempre está vacía. –

+0

@NickOlsen Eso es triste. Actualicé mi respuesta con alguna "solución alternativa". – nemesv

+0

Eso tampoco funciona como metadata. La propiedad del modelo es solo el valor 0. Usando su lógica, pude encontrar otra manera de hacerlo (ver otra respuesta) pero realmente no me gusta. Espero que haya una mejor manera que otra persona pueda brindar. –

6

A partir de ASP.NET MVC 4 Así es como me las arreglé para conseguir la otra propiedad:

PropertyInfo otherPropertyInfo = 
        this.Metadata.ContainerType.GetProperty(attribute.DependentProperty); 

Entonces tuve la Display attribute de la propiedad:

var displayAttribute = 
    otherPropertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true). 
    FirstOrDefault() as DisplayProperty; 

En su caso:

// GetName() is important to get the translated name if you're using a resource file... 
this.otherPropertyDisplayName = displayAttribute.GetName(); 

GetName() referencia:

http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute.name%28v=vs.95%29.aspx

+0

¿Estaba destinado a ser "como DisplayProperty" o "como DisplayAttribute"? El mío no funcionó para DisplayProperty, pero funcionó bien para DisplayAttribute. Gran solución de lo contrario. – MVCKarl

+0

@MVCKarl: Creo que tienes razón ... Tal vez fue un poco confuso al escribir la respuesta aquí. No puedo grabar lo que hice exactamente en ese momento. :) –

Cuestiones relacionadas