2011-12-13 11 views
14

Sto tentando di implementare una strategia di convalida nella mia applicazione. Ho un livello MVC, livello di servizio, repository e dominio POCO. Ora, nel livello MVC, utilizzo le annotazioni dei dati sui miei modelli di visualizzazione per convalidare l'input dell'utente, permettendomi di fornire un feedback rapido all'utente. Nel controller, chiamo ModelState.IsValid per verificare l'input prima di usare automapper per impostare un oggetto dominio.Convalida livello servizio

Ecco dove il mio problema è. Trasmetto il mio oggetto dominio al servizio che deve convalidarlo rispetto alle mie regole aziendali, ma come faccio a inoltrare gli errori di convalida al controller? Gli esempi che ho trovato eseguono una delle seguenti operazioni:

  • Genera un'eccezione nel livello Servizio e cattura il Contoller. Ma questo sembra sbagliato, sicuramente le eccezioni sono per casi eccezionali, e dovremmo restituire qualcosa di significativo.
  • Utilizzare ModelStateWrapper e iniettare ModelStateDictionary nel servizio. Ma questo metodo alla fine si sviluppa in una dipendenza circolare (il controllore dipende dal servizio, il servizio dipende dal controller) e questo sembra essere un cattivo odore di codice.
  • Aggiungere un metodo di convalida a POCO. Il problema è che una regola aziendale può fare affidamento su altri oggetti POCO, quindi sicuramente questo dovrebbe essere fatto nel Servizio che ha accesso alle tabelle e agli oggetti richiesti.

C'è un metodo più semplice che mi manca? Ho visto molte domande su questo, ma nessuna soluzione concreta oltre a quelle sopra menzionate. Sto pensando che qualsiasi metodo nel Servizio che esegue la convalida potrebbe solo restituire un oggetto chiave/valore che posso usare nel controller, ma non sono sicuro se questa strategia potrebbe essere problematica in seguito.

+0

È possibile trovare la seguente risposta utile: http://stackoverflow.com/a/4851953/29407 –

+0

Grazie a Darin, questo sembra promettente, anche se si tratta dell'approccio Eccezione. È questo il modo comunemente accettato per fare questo? – James

risposta

13

Penso piuttosto un modo elegante sarebbe quella di avere un metodo validate sul vostro servizio che restituisce un dizionario di errori di modello dalla logica di business. In questo modo, non viene eseguita l'iniezione di ModelState al servizio: il servizio esegue solo la convalida e restituisce eventuali errori. Spetta quindi al controllore unire questi errori ModelState nel suo ViewData.

Così il metodo di convalida del servizio potrebbe essere simile:

public IDictionary<string, string> ValidatePerson(Person person) 
{ 
    Dictionary<string, string> errors = new Dictionary<string, string>(); 

    // Do some validation, e.g. check if the person already exists etc etc 

    // Add model erros e.g.: 
    errors.Add("Email", "This person already exists"); 
} 

E allora si potrebbe utilizzare un metodo di estensione nel controller per mappare questi errori sul ModelState, qualcosa come:

public static class ModelStateDictionaryExtensions 
{ 
    public static void Merge(this ModelStateDictionary modelState, IDictionary<string, string> dictionary, string prefix) 
    { 
     foreach (var item in dictionary) 
     { 
      modelState.AddModelError((string.IsNullOrEmpty(prefix) ? "" : (prefix + ".")) + item.Key, item.Value); 
     } 
    } 
} 

E Quindi il controller utilizzerà:

ModelState.Merge(personService.ValidatePerson(person), ""); 
+0

Questo dovrebbe essere chiamato dal controller prima di utilizzare Service.Create() (ad esempio)? Ma che cosa succede se Service.Create() ha una logica di business specializzata che fallisce nell'oggetto passato - come potremmo informare il controller? Mi sembra che ogni metodo di servizio richieda le proprie convalide uniche e quindi un modo per informare il controllore dei problemi. Suppongo che i miei metodi di servizio possano restituire IDictionary? – James

+0

Sì, è possibile chiamare Validazione separatamente, ma la convalida deve essere chiamata anche dal servizio stesso nel metodo di creazione. In questo modo, il metodo di creazione potrebbe anche restituire un dizionario di errori. Se il dizionario è vuoto, la convalida ha avuto esito positivo. –

+0

Penso che questa potrebbe essere la strada da percorrere. Non voglio lanciare eccezioni senza una buona ragione come suggerito in molte guide. Hai usato questo metodo te stesso? La mia preoccupazione principale è che se un metodo di servizio ha già un tipo di reso, allora non sarò in grado di restituire IDictionary. Credo che questo sia il motivo per cui alcune guide utilizzano ModelStateWrapper. – James

1

In alternativa a cre a un dizionario intermedio come suggerito da Ian, è possibile farlo anche con un validatore che accetta una funzione.

ad es.nel livello di servizio:

public void ValidateModel(Customer customer, Action<string, string> AddModelError) 
{ 
    if (customer.Email == null) AddModelError("Email", "Hey you forgot your email address."); 
} 

Poi nel controller si convalida con una sola chiamata:

myService.ValidateModel(model, ModelState.AddModelError); 

o dire che si desidera utilizzare il validatore in una console app senza accesso a un ModelStateDictionary, si potrebbe fare questo:

errors = new NameValueDictionary(); 
myService.ValidateModel(model, errors.Add); 

Entrambi questi lavori perché ModelStateDictionary.AddModelError() e NameValueDictionary.Add() 012.corrisponde alla firma del metodo per Action<string, string>.

Problemi correlati