15

Sto cercando di aderire alle migliori pratiche di progettazione multistrato e non voglio che il mio controller MVC interagisca con il mio DAL (o qualsiasi altro IRepository). Deve passare attraverso il mio livello di servizio aziendale per applicare regole e convalida aziendali corrette. Convalida - Non voglio eseguire la convalida nel controller usando i vari attributi di convalida (come [Required]) sulle entità del mio dominio perché questo fa luce sul mio front-end. Per non parlare di questo servizio può anche essere implementato attraverso un front-end WPF.Convalida MVC - Mantenerlo ASCIUTTO con un livello di servizio: qual è la migliore pratica?

Poiché la convalida viene eseguita nel mio livello di servizio, quali sono le migliori pratiche per restituire i valori all'interfaccia utente? Non voglio un 'addWhatever (int somethingsID) vuoto, perché ho bisogno di sapere se ha fallito. Dovrebbe essere un booleano? Dovrebbe essere un Enum? Dovrei approfittare della gestione delle eccezioni? O dovrei restituire qualche oggetto IValidationDictionary simile a quello usato da MVC quando adornano gli attributi di validazione sugli oggetti Model? (che potrei usare un modello di adattatore nell'interfaccia utente in un secondo momento, se necessario)

Vorrei passare la mia entità dal controller al livello di servizio e capire se la convalida/persistenza dati non è riuscita. Inoltre, non voglio perdere di vista il fatto che ho bisogno di restituire una vista che indichi i messaggi di errore corretti per ogni campo che può avere convalida fallita (vorrei mantenerlo il più indolore possibile).

Ho avuto diverse idee, tutte cose che non mi sembrano giuste. Sento che la risposta include entità modello View-specific, ma questo porta a un intero problema di mappatura che deve essere affrontato, per non parlare di ciò viola il principio DRY (Non ripeterlo). Qual è la migliore pratica?

risposta

17

Lo so, sembra che fare validazione MVC viola DRY, ma in realtà .. non lo fa .. almeno non per la maggior parte delle applicazioni (non banali).

Perché? Poiché i requisiti di convalida della tua vista sono abbastanza diversi dai requisiti di convalida dei tuoi oggetti di business. La convalida delle visualizzazioni riguarda se stessa per convalidare che una vista specifica è valida, non che il modello di business è valido.

A volte questi due sono uguali, ma se si crea l'app in modo che la vista richieda che il modello di business sia valido, allora ci si sta bloccando in questo scenario. Cosa succede se è necessario dividere la creazione dell'oggetto in due pagine? Cosa succede se si decide di utilizzare il livello di servizio per un servizio Web? Bloccando l'interfaccia utente in uno scenario di convalida del livello aziendale, si compromettono seriamente i tipi di soluzioni che è possibile fornire.

La vista è la convalida dell'input, non la convalida del modello.

+0

Anche se questa è una domanda piuttosto vecchia, mi piacerebbe avere un piccolo chiarimento sull'argomento. Diciamo che devo convalidare che un 'username' non deve superare i 15 caratteri e dovrebbe essere unico. La restrizione di 15 caratteri sarebbe avvenuta nel controller e la verifica dell'unicità sarebbe avvenuta nel servizio? – Aquillo

+0

@Aquillo - No, confermeresti entrambi in entrambe le posizioni. Questa risposta riguarda le visualizzazioni e la convalida in generale, non su elementi specifici. MVC convalida un "modello" non una proprietà, un modello è costituito da 0 o più proprietà che possono avere 0 o più convalide. MVC dice solo "Questo modello è valido". –

4

Si consiglia di sfruttare la convalida MVC integrata decorando le classi del modello con annotazioni di dati. Questo è solo per la convalida dell'input di base che è diversa dall'elaborazione di regole e convalida aziendali. Le annotazioni di dati sono grandi perché sono utili per qualsiasi consumatore consapevole, ma non hanno un impatto negativo sui consumatori che non comprendono come utilizzarle.

Penso che tu abbia ragione di utilizzare un livello di servizio per astrarre le regole aziendali e l'accesso ai dati. Si consiglia di fare un paio di cose per migliorare l'interazione tra controllore e di servizio:

  1. ritorno XXXResult oggetti invece di nulla o primitive. Se il metodo di servizio è AddProduct, restituire AddProductResult o più ProductServiceOperationResult in generale. Questo risultato contiene un indicatore di successo/fallimento e ulteriori informazioni.

  2. Se si utilizza WCF, utilizzare Contratti di guasto ed eccezioni.

Una tipica soluzione MVC Applicazione mio assomiglia a questo:

  • MVC sito web del progetto
  • xxx.Model (progetto, a cui fa riferimento la maggior parte degli strati)
  • xxx.Services (progetto)
  • xxx.DataAccess (progetto, talvolta unione con servizi)
  • altri secondo necessità

Buona fortuna!

6

Ecco come l'ho fatto.

Fare in modo che il livello di servizio esegua eccezioni in caso di errore della regola aziendale/della regola di convalida. Crea la tua validazione Eccezione per questo e includi alcune proprietà per contenere i dettagli dell'errore di convalida - (ad esempio quale proprietà ha l'errore di convalida e qual è il messaggio)

Quindi creare un metodo di estensione su Eccezione che copierà il dettagli dell'errore al ModelState (ho avuto questa idea da Steve Sandersons piuttosto eccellente libro "Pro Asp.Net MVC 2 Framework") - se fai questo, MVC evidenzierà i campi non validi, mostrerà errori nell'interfaccia utente, ecc.

Poi il controller conterrà qualcosa come questo

try 
{ 
    Service.DoSomeThing(); 
} 
catch (Exception err) 
{ 
    err.CopyTo(ModelState); 
} 

Ciò significa che y le nostre regole aziendali e la convalida sono ora nel tuo livello di servizio, e questo potrebbe essere riutilizzato.

Considera anche il passaggio di DTO/Visualizza modelli alle tue visualizzazioni e la mappatura degli oggetti di dominio su DTO e (viceversa), piuttosto che passare gli oggetti di dominio alle visualizzazioni.

Quindi i DTO/Visualizza i modelli possono risiedere nel livello MVC, e puoi decorarli con gli attributi di convalida e fare in modo che il controller li passi alle viste, utilizzando quindi la convalida MVC integrata.

Troverete che per qualsiasi progetto complesso, la convalida richiesta all'interfaccia utente potrebbe essere leggermente diversa da quella richiesta alle regole aziendali, quindi questo aiuta a separarlo.

C'è una buona libreria denominata AutoMapper che semplifica la mappatura dagli oggetti di dominio ai DTO (e viceversa) senza molto codice di codice.

+1

Hmm, non sono sicuro che i dati non validi siano "eccezionali" e degni di un'eccezione. Perché scegliete le eccezioni sulla restituzione di un risultato o utilizzando le interfacce di convalida incorporate? –

+3

Sono d'accordo, è più che qualsiasi dato non valido non catturato dalla validazione del layer MVC è eccezionale e viene catturato dal livello di serice. Sto attualmente utilizzando NHibernate.Validator per il lato di validazione delle cose, che genera eccezioni nel datalayer. Sto usando le interfacce di validazione integrate anche nel livello MVC. L'idea è il layer MVC _should_ cattura gli errori di input usando l'annotazione dei dati incorporata, ma se i dati non validi superano il livello MVC, il livello di servizio genererà un'eccezione – StanK

+0

Ok, che ha più senso. Personalmente, non faccio le cose in questo modo, ma vedo il merito nel tuo chiarimento. –

2

Il problema qui è la convalida nel livello di servizio, ma come ottenere che tali informazioni "eseguano il backup" nell'app Web. Qualcosa di simile abbiamo discusso un po 'da quando l'idea di dipendenza da dipendenza entra chiaramente in gioco qui se un servizio sta convalidando, non è possibile chiamare sinply il servizio nel Modello (ad esempio se IValidateableObject è implementato lì non si desidera chiamare il il servizio direttamente)

l'approccio adottato è stato:

Opzione 3: non sapevo di questo prima, ma quello che sembra essere un modo molto potente scrivere validatori è quello di utilizzare la classe ModelValidator e un corrispondente ModelValidatorProvider.

ASP.NET MVC 3: Validating model when information external to the model is required

Quindi, in pratica si sta iniettando un validatore (che sarebbe poi essere nel vostro livello di servizio) da risolvere con MVC, senza la necessità di una chiamata esplicita servizio di individuazione.

0

In realtà non è una cattiva idea eseguire ripetutamente convalide in pochi livelli (lato client, lato server nel controller o equivalente e di nuovo nel livello aziendale). Rende il tuo codice un po 'disgiunto. Idealmente dovresti solo descriverli in un posto, ma a volte questo non è possibile. Non riuscendo ad utilizzare le annotazioni dei dati, lo stai rendendo davvero difficile se vuoi fare convalide sul lato client? Sembra così.

In ogni caso, ciò che ho fatto in passato nelle applicazioni non-mvc è che la maggior parte dei metodi di azione restituisce un oggetto Response che include uno stato (successo, errori, avvisi) e un elenco di errori di convalida, nonché qualsiasi altre proprietà richieste.

Potresti essere in grado di sfruttare l'interfaccia IValidateableObject, ma questo ti lega di nuovo a qualcosa di specifico di ASP.net. Forse un compromesso sarebbe quello di consumare il tuo oggetto risposta e convertirlo in errori specifici di DataAnnotation.

1

I consigli di Stephen sono perfetti in questo scenario. Attualmente sto lavorando su un'applicazione MVC 3.0 molto grande con SOA e altre cose sono coinvolte. Quindi, in una risposta, vorresti riempire tutte le informazioni necessarie e mostrarle ai tuoi punti di vista (il controller del corso detterà). Spero che questo ti aiuti.

Problemi correlati