2009-08-18 6 views
23

Attualmente sto passando i miei oggetti di dominio alle mie viste e lego direttamente a loro da POST. Tutti dicono che questo è male, quindi sto tentando di aggiungere il concetto di ViewModel.utilizzando ViewModels per le azioni POST in MVC elegantemente

Tuttavia, non riesco a trovare un modo per farlo in modo molto elegante, e mi piacerebbe sapere quali sono le soluzioni di altre persone per non finire con un'azione di controller molto caotica.

il tipico processo per dire alcune funzionalità "Aggiungi persona" si presenta così:

  1. fare una richiesta GET per una vista che rappresenta un ViewModel persona vuota
  2. palo (in) i dati validi
  3. controller rilegge dati registrati su una persona viewmodel
  4. se l'associazione fallisce, ho bisogno di fare la stessa azione come in (1) ma con alcuni dati, non un oggetto vuoto ed errori
  5. se l'associazione ha superato, i bisogno di mappare le proprietà dal VM su un modello reale
  6. validare il modello
  7. se la convalida superato: salvare la persona, si impegnano, mappare i dettagli degli utenti ad un display VM e restituirlo in una vista
  8. se la convalida fallito, esegui le stesse azioni di (1) ma con alcuni dati ed errori

Fare tutto questo in un'azione del controller (ignorando il GET) non è certamente SRP o DRY.

Sto cercando di pensare a un modo per rompere questo processo in modo che rispetti SRP, sia pulito, modulare e soprattutto verificabile.

Quali sono le soluzioni per i popoli?

Ho sperimentato con i selettori di azioni personalizzate del controller per separare le preoccupazioni in singoli metodi, smart modelbinders e semplicemente brute force ma non ho ancora trovato una soluzione soddisfacente.

P.S. come aggiunge tanta complessità, convincimi perché ho anche bisogno di infastidire

+0

Che cosa si finisce per fare? –

+1

ancora niente :(sto ancora cercando di decidere su una soluzione elegante in modo che i miei controller non finiscano davvero disordinati Penso che la vera risposta sia openrasta –

+0

Forse questi post possono aiutare un po ': http://stackoverflow.com/a/25460769/3969501, http://stackoverflow.com/a/25169023/1475331 –

risposta

2

Il pattern MVVM (ViewModel) è sicuramente quello per cui andare, ho avuto una domanda simile su POST di tornare a un'azione pochi giorni indietro - qui è il collegamento: MVVM and ModelBinders in the ASP.NET MVC Framework

Il risultato è stato che è possibile utilizzare l'attributo Bind per registrare il tipo complesso desiderato.

6

Ho sentito lo stesso disagio. Il mio unico modo intorno ad esso è stato quello di effettuare le seguenti operazioni:

  1. Creare un legante per legare e validare il modello di vista
  2. Creare un legante per ottenere l'entità dal database (o semplicemente fare questo nel controllore)
  3. Chiamare un metodo di salvataggio ereditato nella superclasse. Questo metodo prende il viewmodel e l'entità che verrà aggiornata e fa tutto il lavoro che hai elencato nei tuoi passi.

Procedimento azione aspetto:

public ActionResult Whatever(TViewModel viewModel, TEntity entity) 
{ 
    return Save(viewModel, entity); 
} 

Il controller di base ha una definizione generica, così:

public abstract BaseController<TEntity, TViewModel> 
    where TEntity : Entity 
    where TViewModel : ViewModel 

Il costruttore ha due dipendenze, uno per il repository entità e un altro per il mappatore del modello, in questo modo:

protected BaseController(IRepository<TEntity> repository, IMapper<TEntity, TViewModel> mapper) 

Con questo in luogo, è possibile scrivere un Salva metodo protetto che può essere chiamato dalle azioni di controllo nella sottoclasse, in questo modo:

protected ActionResult Save(TViewModel viewModel, TEntity entity) 
{ 
    if (!ModelState.IsValid) 
     return View(viewModel); 

    _mapper.Map(viewModel, entity); 
    if (!entity.IsValid) 
    { 
     // add errors to model state 
     return View(viewModel); 
    } 

    try 
    { 
     _repository.Save(entity); 
     // either redirect with static url or add virtual method for defining redirect in subclass. 
    } 
    catch (Exception) 
    { 
     // do something here with the exception 
     return View(viewModel); 
    } 
} 

Per quanto riguarda la verificabilità, è possibile verificare il metodo di salvataggio che passa valida/modelli di visualizzazione e entità non valide. È possibile testare l'implementazione del model mapper, lo stato valido del modello di vista e lo stato valido dell'entità separatamente.

Rendendo generico il controller di base, è possibile ripetere questo modello per ogni combinazione di entità/viewmodel nel dominio, se si stanno creando molti controller per fare la stessa cosa.

Sono molto interessato a sapere cosa gli altri hanno da dire su questo. Grande domanda.

+0

idee interessanti +1 –

+2

Idee decisamente interessanti, ma OpenRASTA è ancora un'opzione migliore :) – Dve

0

ho molte buone soluzioni nell'applicazione di esempio MVC asp.net che si trova nel download of valueinjecter (mapper che io uso per mappare ViewModels da/per entità, è possibile anche mappare FormCollection/Richiesta di Enti)

ecco uno:

public class TinyController :Controller 
     { 
      private readonly IModelBuilder<Person, PersonViewModel> modelBuilder; 

      public TinyController() 
      { 
       modelBuilder = new PersonModelBuilder(); 
      } 

      public ActionResult Index() 
      { 
       return View(modelBuilder.BuildModel(new PersonRepository().Get())); 
      } 

      [HttpPost] 
      public ActionResult Index(PersonViewModel model) 
      { 
       if (!ModelState.IsValid) 
        return View(modelBuilder.RebuildModel(model)); 

        var entity = modelBuilder.BuildEntity(model); 
... 
//save it or whatever 
      } 
     } 
Problemi correlati