Domanda

Quando ho iniziato a usare xVal per la convalida lato client, stavo solo implementando metodi di azione che utilizzavano oggetti del modello di dominio come viewmodel o istanze incorporate di tali oggetti nel viewmodel.

Questo approccio funziona bene nella maggior parte dei casi, ma ci sono casi in cui la vista deve visualizzare e pubblicare solo un sottoinsieme delle proprietà del modello (ad esempio quando l'utente desidera aggiornare la propria password, ma non il resto dei dati del proprio profilo) .

Una soluzione (brutta) consiste nell'avere un campo di input nascosto nel modulo per ogni proprietà che non sarebbe altrimenti presente nel modulo.

Apparentemente la migliore pratica qui è creare un viewmodel personalizzato che contenga solo proprietà rilevanti per la vista e popolare il viewmodel tramite Mappatore automatico.È molto più pulito poiché trasferisco solo i dati rilevanti per la vista, ma è tutt'altro che perfetto poiché devo ripetere gli stessi attributi di convalida già presenti sull'oggetto del modello di dominio.

Idealmente, mi piacerebbe specificare l'oggetto del modello di dominio come metaclasse tramite un attributo MetaData (spesso chiamato anche "classe buddy"), ma ciò non funziona poiché xVal genera un'eccezione quando la classe di metadati ha proprietà che sono non presente sul viewmodel.

Esiste una soluzione elegante a questo?Ho preso in considerazione l'idea di hackerare il codice sorgente di xVal, ma forse c'è qualche altro modo che finora ho trascurato.

Grazie,

Adriano

Modificare: Con l'arrivo di ASP.NET MVC 2, questo non è più solo un problema legato agli attributi di convalida, ma si applica anche all'editor e agli attributi di visualizzazione.

È stato utile?

Soluzione

Questo è il motivo per cui le schermate per eccellenza ingresso non devono essere strettamente accoppiati al modello. Questa domanda in realtà si apre qui sul tag MVC circa 3-4 volte al mese. Mi piacerebbe ingannare se riuscivo a trovare la domanda precedente e alcuni del commento discussione qui è interessante. ;)

Il problema vostro avere è che si sta cercando di forzare due diversi contesti validazione di un modello in un unico modello che non riesce in una grande quantità di scenari. L'esempio migliore è la firma di un nuovo utente e poi avere un admin modificare un campo utente tardi. È necessario per convalidare una password su un oggetto utente durante la registrazione, ma non sarà possibile visualizzare il campo password per l'amministratore modifica dei dati utente.

Le scelte per muoversi questi sono tutti i sub-ottimale. Ho lavorato su questo problema per 3 progetti ora e l'attuazione delle seguenti soluzioni non è mai stato pulito e di solito frustrante. Io vado a cercare di essere pratica e dimenticare tutto il / modello / discussioni hotnessofthemonth DDD / db tutti gli altri sta avendo.

1) più Visualizza i modelli  Avere ViewModels che sono quasi la stessa viola il principio DRY ma mi sento i costi di questo approccio sono veramente bassi. Di solito violare DRY amplifica i costi di manutenzione, ma secondo me i costi per questo sono i più bassi e non un gran che. Ipoteticamente parlando non cambiare il modo massimo numero caratteri del campo Cognome può avere molto spesso.

2) Dinamico metadati Ci sono ganci in MVC 2 per fornire il proprio metadati per un modello. Con questo approccio si potrebbe avere qualunque sia il vostro tramite per fornire i metadati escludere determinati campi in base alla corrente HTTPRequest e quindi di azione e di controllo. Ho usato questa tecnica per costruire un sistema di autorizzazioni per il database motore che va al DB e racconta la una sottoclasse del DataAnnotationsMetadataProvider di escludere le proprietà valori memorizzati nel database basati.

Questa tecnica sta lavorando molto atm, ma l'unico problema sta convalidando con UpdateModel(). Per risolvere questo problema abbiamo creato un metodo SmartUpdateModel() che va anche al database e genera automaticamente l'escludere string [] array in modo che eventuali campi non permissisable non vengono convalidati. Noi, naturalmente, memorizzati nella cache questo per motivi di prestazioni quindi non è male.

Voglio solo ribadire che abbiamo usato [ValidationAttributes] sui nostri modelli e poi li superceeded con le nuove norme in materia di esecuzione. Il risultato finale è stato che il campo [Required] User.LastName non è stato convalidato se l'utente non ha avuto il permesso di accedervi.

3) Pazzo interfaccia dinamica Proxy Thing L'ultima tecnica ho cercato di era quello di utilizzare le interfacce per ViewModel. Il risultato finale è che ho avuto un oggetto utente che ha ereditato da interfacce come IAdminEdit e IUserRegistration. IAdminEdit e IUserRegistration sarebbero entrambi contenere attributi DataAnnotation che si sono esibiti tutti i convalida specifico contesto come una proprietà Password con le interfacce.

Ciò ha richiesto un po 'di aggiustamenti ed era più un esercizio accademico di ogni altra cosa. Il problema con 2 e 3 è che UpdateModel e il provider DataAnnotationsAttribute necessario essere adattato per essere messo a conoscenza di questa tecnica.

Il mio più grande ostacolo è stato non ho mai voglia di inviare l'oggetto utente intera alla vista così ho finito per usare proxy dinamici per creare istanze di runtime del IAdminEdit

Ora capisco questa è una domanda specifica molto xVal ma tutte le strade per la convalida dinamico come questo portare ad personalizzazione dei fornitori interni MVC metadati. Dal momento che tutte le cose metadati è nuovo nulla è così pulito o semplice da fare a questo punto. Il lavoro che avrebbe dovuto fare per personalizzare il comportamento di convalida del MVC non è difficile, ma richiede una certa conoscenza approfondita di come tutto il lavoro interni.

Altri suggerimenti

Abbiamo spostato la nostra convalida attribuisce allo strato ViewModel. Nel nostro caso, questo ha fornito una separazione più pulita delle preoccupazioni in ogni caso, come siamo stati poi in grado di progettare il nostro modello di dominio tale che non poteva entrare in uno stato non valido, in primo luogo. Ad esempio, data potrebbe essere richiesto su un oggetto BillingTransaction. Quindi noi non vogliamo rendere Nullable. Ma il nostro ViewModel, potremmo aver bisogno di esporre Nullable tale che siamo in grado di prendere la situazione in cui l'utente non ha inserito un valore.

In altri casi, si potrebbe avere la convalida che è specifico per pagina / modulo, e si vorrà per convalidare in base al comando l'utente sta tentando di eseguire, piuttosto che impostare un mucchio di roba e chiedere il modello di dominio, "sei valido per cercare di fare XYZ", dove nel fare "ABC" quei valori sono validi.

Se i ViewModel ti vengono ipoteticamente imposti, ti consiglio di applicare solo requisiti indipendenti dal dominio.Ciò include cose come "il nome utente è obbligatorio" e "l'e-mail è formattata correttamente".

Se duplichi la convalida dai modelli di dominio nei modelli di visualizzazione, hai strettamente associato il dominio all'interfaccia utente.Quando la convalida del dominio cambia ("può applicare solo 2 coupon a settimana" diventa "può applicare solo 1 coupon a settimana"), l'interfaccia utente deve essere aggiornata.In generale, questo sarebbe terribile e dannoso per l’agilità.

Se sposti la convalida dai modelli di dominio all'interfaccia utente, hai sostanzialmente sventrato il tuo dominio e hai affidato la responsabilità della convalida all'interfaccia utente.Una seconda interfaccia utente dovrebbe duplicare tutta la convalida e hai accoppiato insieme due interfacce utente separate.Ora, se il cliente desidera un'interfaccia speciale per amministrare l'inventario dal proprio iPhone, il progetto iPhone deve replicare tutta la convalida che si trova anche nell'interfaccia utente del sito web.Ciò sarebbe ancora più terribile della duplicazione della convalida descritta sopra.

A meno che tu non possa prevedere il futuro ed escludere queste possibilità, convalida solo i requisiti indipendenti dal dominio.

Non so come andrà a giocare per la validazione lato client, ma se la convalida parziale è il tuo problema è possibile modificare la DataAnnotationsValidationRunner discusso qui a prendere in un elenco IEnumerable<string> dei nomi di proprietà, come segue:

public static class DataAnnotationsValidationRunner
{
     public static IEnumerable<ErrorInfo> GetErrors(object instance, IEnumerable<string> fieldsToValidate)
     {
           return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>().Where(p => fieldsToValidate.Contains(p.Name))
                  from attribute in prop.Attributes.OfType<ValidationAttribute>()
                  where !attribute.IsValid(prop.GetValue(instance))
                  select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
     }
}

Sono rischio andando le downvotes e lo stato che non v'è alcun beneficio per ViewModels (in ASP.NET MVC), soprattutto se si considera l'overhead di creazione e mantenimento delle stesse. Se l'idea è quella di disaccoppiare dal dominio, che è indifendibile. Un utente disaccoppiato da un dominio non è un'interfaccia utente per il dominio. L'interfaccia utente deve dipendono dal dominio, in modo che stai sia andando ad avere le vostre opinioni / Azioni accoppiate al modello di dominio, o la logica di gestione ViewModel accoppiato al modello di dominio. L'argomento architettura è quindi discutibile.

Se l'idea è quella di impedire agli utenti di hacker POST HTTP maligni che sfruttano il modello di ASP.NET MVC legame di mutare i campi non dovrebbero essere autorizzati a cambiare, allora A) il dominio dovrebbe far rispettare questo requisito, e B) le azioni dovrebbero fornire whitelist di proprietà aggiornabili al modello legante.

A meno che siate dominio sta esponendo qualcosa di pazzo come un live, in memoria oggetto grafico invece di copie di entità, ViewModels sono sprecati sforzo. Quindi, per rispondere alla tua domanda, mantenere la convalida del dominio nel modello di dominio.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top