Problema di convalida del modello MVC 3 – Oversight o By Design

Mi sono imbattuto in uno scenario in cui avevo bisogno di sapere quale proprietà era attualmente convalidata in un ValidationAttribute personalizzato. Ho pensato che sarebbe stato facile con MVC 3 dato che ValidationContext è stato passato nel metodo IsValid .

Senza entrare nei dettagli, ecco l’idea di base:

 protected override ValidationResult IsValid(Object value, ValidationContext validationContext) { if (ShouldICareAboutYou(validationContext.MemberName)) { //Do some stuff } //Return the results } 

Sembrava la soluzione perfetta e, in effetti, quando l’unità testava il mio personalizzato ValidationAttribute usando Validator.TryValidateObject tutto funzionava perfettamente!

PERÒ…

Quando si chiama TryUpdateModel o TryValidateModel nel mio controller, viene eseguita la convalida, ma ValidationContext.MemberName è null.

Whaa Huh?!?

Ho fatto una piccola indagine e abbastanza sicuro, proprio all’interno di DataAnnotationsModelValidator c’è il codice … o la sua mancanza.

 public override IEnumerable Validate(object container) { // Per the WCF RIA Services team, instance can never be null (if you have // no parent, you pass yourself for the "instance" parameter). ValidationContext context = new ValidationContext(container ?? Metadata.Model, null, null); context.DisplayName = Metadata.GetDisplayName(); // Setting the MemberName here would be trivial! // However, philh told me not to. Something about // a guy named Josh who pushed him down on the playground // in middle school. //context.MemberName = Metadata.PropertyName; (Suck It, Josh!!!) ValidationResult result = Attribute.GetValidationResult(Metadata.Model, context); if (result != ValidationResult.Success) { yield return new ModelValidationResult { Message = result.ErrorMessage }; } } 

Mi rendo conto che DisplayName potrebbe essere il nome della proprietà se non è stato applicato DisplayAttribute alla proprietà. Sfortunatamente non posso davvero trattare ipotesi. Devo sapere esattamente qual è il nome della proprietà.

Allora, qual è l’accordo? È questo in base alla progettazione o a una supervisione onesta. Se si tratta di una svista, sarebbe fantastico farlo correggere in MVC 4 🙂

Disclaimer:

I commenti aggiunti nel codice di esempio sopra sono pensati per essere divertenti. Non lo so, né ho mai incontrato Phil Haack . Da quello che posso dire sembra un ragazzo davvero simpatico. E spingerlo giù nella scuola media mi avrebbe fatto fare una doccia reale!

Ho avuto questo stesso problema e ho deciso di passare il nome della proprietà come parametro nel costruttore di attributi e quindi memorizzarlo nell’attributo. Per esempio:

 [MyValidationAttribute("MyProperty")] public string MyProperty { get; set; } 

Quindi in MyValidationAttribute.cs:

 public class MyValidationAttribute { private string PropertyName; public MyValidationAttribute(string propertyName) { this.PropertyName = propertyName; } } 

È un po ‘fastidioso che ora devo digitare il nome della mia proprietà due volte ma risolve il problema.

Josh,

Frustrante, sì

Tuttavia, per i tuoi scopi, puoi creare la tua class ereditando da DataAnnotationsModelValidator , sovrascrivi il metodo Validate() e decommenta quella linea di codice che ti sta deridendo. Quindi, in Global.asax.cs, deselezionare ModelValidatorProviders.Providers e aggiungere la class.

Non una soluzione ottimale, ma una che ti porterà dove devi andare.

È necessario chiamare DataAnnotationsModelValidatorProvider.RegisterAdapter o DataAnnotationsModelValidatorProvider.RegisterAdapterFactory per il tipo di attributo e fornire ModelValidator personalizzato.

Lo stesso problema e questa domanda mi hanno messo sulla strada giusta. Ho risolto il problema modificando il mio validatore personalizzato per popolare MemberName quando ValidationResult è stato creato, ad esempio (notare il secondo parametro nel costruttore ValidationResult):

 protected override ValidationResult IsValid(Object value, ValidationContext validationContext) { if (ShouldICareAboutYou(validationContext.MemberName)) { //Do some stuff } return new ValidationResult(FormatErrorMessage(validationContext.DisplayName), new string[] { validationContext.MemberName }); }