6

Ho creato un attributo di convalida CompareLessThan personalizzato mediante la copia di ASP.NET MVC 3 CompareAttribute e, invece di verificare l'uguaglianza, controllo che una proprietà sia inferiore a un'altra. Se c'è un errore lato client, il messaggio '{0} deve essere inferiore a {1}' viene visualizzato all'utente.ASP.NET MVC ValidationAttribute Ottiene l'altro nome visualizzato di proprietà

Il mio modello è configurato come segue con gli attributi di visualizzazione che fanno riferimento a un file di risorse.

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

Poi il metodo GetClientValidationRules di validazione è esattamente lo stesso come nel CompareAttribute

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

Qui stiamo generando il messaggio di errore che verrà visualizzato all'utente se c'è un problema. Posso ottenere il nome visualizzato dal file di risorse per la proprietà che è decorata con il mio attributo personalizzato CompareLessThan, ma la mia domanda è: come posso ottenere il nome visualizzato della proprietà "altra" con cui confrontiamo? Nel metodo IsValid abbiamo un riferimento al validationContext da cui posso generare un oggetto PropertyInfo per la proprietà 'other' e penso di ottenere il nome visualizzato. Ma nel GetClientValidationRules non ho accesso a quello.

Potrei sempre passare un altro valore per il nome visualizzato dell'altra proprietà, ma speravo che ci sarebbe stato un modo per derivarlo come sto già specificando con annotazioni di dati.

Qualche idea?

risposta

5

La risposta fornita da nemesv non ha funzionato come la proprietà metadata.Model ha un valore pari a 0. Ma, attraverso i metadati noi abbiamo il nome completo del modello per cui è possibile creare un nuovo istanza di quel modello e quindi creare un nuovo DataAnnonationsModelMetadataProvider da tale istanza di creazione. Da lì possiamo ottenere il nome visualizzato dell'altra proprietà.

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

Non mi piace molto questa soluzione (anche se funziona) in quanto sembra che ci dovrebbe essere un modo migliore. Qualcun altro ha altre idee?

4

non l'ho provato fuori ma è possibile ottenere le proprietà modello con il metadata.Properties proprietà

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

EDIT: A causa delle proprietà è vuota quello che si può sempre fare (anche se è molto elegante). Puoi generare i metadati per te stesso.

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

Buona idea, ma la collezione di proprietà è sempre vuota. –

+0

@NickOlsen Questo è triste. Ho aggiornato la mia risposta con qualche "soluzione". – nemesv

+0

Questo non funziona come la proprietà metadata.Model è solo il valore 0. Usando la tua logica sono stato in grado di capire un altro modo per farlo (vedi altra risposta) ma non mi piace molto. Spero che ci sia un modo migliore che qualcun altro può fornire. –

6

Come di ASP.NET MVC 4 questo è come sono riuscito ad ottenere l'altra proprietà:

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

Poi ho avuto la Display attribute dalla proprietà:

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

Nel tuo caso:

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

GetName() riferimento:

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

+0

Doveva essere "come DisplayProperty" o "come DisplayAttribute"? Il mio non ha funzionato per DisplayProperty ma ha funzionato bene per DisplayAttribute. Ottima soluzione altrimenti. – MVCKarl

+0

@MVCKarl: Penso che tu abbia ragione ... Forse era un po 'di confusione mentre scrivevo la risposta qui. Non riesco a registrare quello che ho fatto esattamente in quel momento. :) –

Problemi correlati