2012-05-22 14 views
8

Ho appena iniziato a utilizzare e mi sto innamorando del modello di progettazione MVC.MVC Riduzione del codice ripetitivo

Il mio unico cruccio è che sembra produrre un sacco di codice ripetitivo. Per esempio.

Ho una MVC App standard con il mio DB (modelli) in un progetto, separato dai miei controller/viste/viewmodels in un altro, nuovamente separato dai miei metodi di test in un altro. Tutto funziona alla grande.

Modelli: Ora ho un bel po 'di belle classi EF4 nel mio progetto DB, che devo usare ViewModels per il mio vero progetto per accedere ai miei dati. Nessun problema qui.

Controllers: Tuttavia, ogni controller che ho, essenzialmente fa la stessa cosa. Ottiene e imposta i dati da ViewModels così mentre ciascun controller è diverso in quanto ottiene solo dati diversi, tutti svolgono essenzialmente lo stesso lavoro, nello stesso modo. (Al momento ne ho 9, ma potrebbe facilmente esplodere a oltre 50).

Ad esempio:

public class Dummy1Controller : Controller 
{ 
    private MyProj.Data.Entities _entities = new Data.Entities(); 
    private MyProj.Data.Entities2 _coreEntities = new Data.Entities2(); 

    //GET: /Customers/ 
    public ActionResult Index() 
    { 
     if (_entities.table1.Count() == 0) return View(); 

     var pastObj = _entities.table1.First(); 
     return View(new Table1ViewModel() 
     { 
      Id = pastObj.Id, 
      FirstName = pastObj.FirstName, 
      LastName = pastObj.LastName, 
      . 
      . 
      . 
      . 
     }); 
    } 
} 

public class Dummy2Controller : Controller 
{ 
    private MyProj.Data.Entities _entities = new Data.Entities(); 
    private MyProj.Data.Entities2 _coreEntities = new Data.Entities2(); 

    //GET: /Vehicles/ 
    public ActionResult Index() 
    { 
     if (_entities.table2.Count() == 0) return View(); 

     var pastObj = _entities.table2.First(); 
     return View(new Table1ViewModel() 
     { 
      RegNo = pastObj.RegNo, 
      Make = pastObj.Make, 
      Model = pastObj.Model, 
      . 
      . 
      . 
      . 
     }); 
    } 
} 

public class Dummy3Controller : Controller 
{ 
    private MyProj.Data.Entities _entities = new Data.Entities(); 
    private MyProj.Data.Entities2 _coreEntities = new Data.Entities2(); 

    //GET: /Invoices/ 
    public ActionResult Index() 
    { 
     if (_entities.table3.Count() == 0) return View(); 

     var pastObj = _entities.table3.First(); 
     return View(new Table1ViewModel() 
     { 
      InvNo = pastObj.InvNo, 
      Amount = pastObj.Amount, 
      Tax = pastObj.Tax, 
      . 
      . 
      . 
      . 
     }); 
    } 
} 

Visualizzazioni: ogni vista generato dai contollers grande lavoro. Execpt, quell'unica cosa che cambia sono i dati (campi con etichette e caselle di testo). Ancora una volta, fanno tutti lo stesso lavoro (ma con set di dati diversi).

@model MyProject.Web.ViewModels.Table1ViewModel 

@{ 
    ViewBag.Title = "Index"; 
} 

<link href="@Url.Content("~/Content/CSS/GenericDetailStyles.css")" rel="stylesheet" type="text/css" /> 

<section id="content"> 
    <div id="table"> 
     <div> 
      <h2>Customer</h2> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.Id)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Id)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.FirstName)</div> 
      <div class="right">@Html.TextBoxFor(model => model.FirstName)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.LastName)</div> 
      <div class="right">@Html.TextBoxFor(model => model.LastName)</div> 
     </div> 
     . 
     . 
     . 
     . 
    </div> 
</section> 

@{Html.RenderAction("Index", "FooterPartial");} 


-------------------------------------------------------------------------------------- 


@model MyProject.Web.ViewModels.Table2ViewModel 

@{ 
    ViewBag.Title = "Index"; 
} 

<link href="@Url.Content("~/Content/CSS/GenericDetailStyles.css")" rel="stylesheet" type="text/css" /> 

<section id="content"> 
    <div id="table"> 
     <div> 
      <h2>Vehicle</h2> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.RegNo)</div> 
      <div class="right">@Html.TextBoxFor(model => model.RegNo)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.Make)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Make)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.PatientID)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Model)</div> 
     </div> 
     . 
     . 
     . 
     . 
    </div> 
</section> 

@{Html.RenderAction("Index", "FooterPartial");} 


-------------------------------------------------------------------------------------- 


@model MyProject.Web.ViewModels.Table3ViewModel 

@{ 
    ViewBag.Title = "Index"; 
} 

<link href="@Url.Content("~/Content/CSS/GenericDetailStyles.css")" rel="stylesheet" type="text/css" /> 

<section id="content"> 
    <div id="table"> 
     <div> 
      <h2>Invoice</h2> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.InvNo)</div> 
      <div class="right">@Html.TextBoxFor(model => model.InvNo)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.Amount)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Amount)</div> 
     </div> 
     <div class="row"> 
      <div class="left">@Html.LabelFor(x=>x.Tax)</div> 
      <div class="right">@Html.TextBoxFor(model => model.Tax)</div> 
     </div> 
     . 
     . 
     . 
     . 
    </div> 
</section> 

@{Html.RenderAction("Index", "FooterPartial");} 

Problema: Voglio fare un singolo controller, e renderlo dinamico. In modo che possa leggere i dati da diversi modelli di vista. (Perché 9 o 50 controller eseguono in modo identico lo stesso lavoro)

Quindi voglio fare lo stesso con le viste. In modo che i diversi campi possano essere generati dinamicamente. (Perché 9 o 50 visualizzazioni fanno tutti lo stesso lavoro). Se la vista è basata sul controller, la vista dovrebbe essere in grado di cambiare in base alle sue proprietà.

In sostanza, tutto quello che voglio fare è trovare il modo di dire al controller di leggere da viewmodel X o VM-Y o VM-Z ect e dovrebbe essere in grado di generare le proprietà, recuperare i dati associati e passarli alla vista, che al momento della ricezione, genererà i campi con etichette e caselle di testo.

Immagino di voler sapere se c'è un modo per farlo usando il riflesso. Poiché i modelli di vista sono classi base con proprietà semplici. Si potrebbe potenzialmente creare una classe controller di base che abbia un metodo per leggere in un oggetto viewmodel specificato, ottenere le sue proprietà, leggere anche una tabella associata e abbinare i campi in quella tabella con le proprietà nella classe. Finalmente si può passare nel record dal tavolo per visualizzare. La vista quindi può essere generata automaticamente in base a questo utilizzando un qualche tipo di rasoio, C# o javascript.

Qualsiasi accorgimento se ciò è possibile o meno sarebbe gradito.

+0

Perché si mescolano il pattern MVC e MVVM? Credo che quello che sto chiedendo sia, a cosa serve il ViewModel e non solo a passare il Modello? – Brunner

+0

@Brunner - Beh, in primo luogo, è come mi è stato mostrato e non so come farlo direttamente (sono relativamente nuovo a MVC). In secondo luogo, il mio datore di lavoro vuole farlo in questo modo. Infine, quando lavoro sui miei progetti, lo faccio in questo modo perché non mi piace avere il DB attuale come parte del progetto come mostrano i molti esempi, e averlo generato o rigenerato automaticamente. Ci sono pochissimi esempi pratici che mostrano come connettersi ad un vero database live. Se ne conosci qualcuno, vorrei davvero appregiare dei link a loro. Grazie per la tua risposta. –

+0

Non sono sicuro di cosa intendi per "dbs live reali", ma potresti voler guardare i POCO in combinazione con EF (ad esempio Code-First) - è più o meno quello che hai già, solo impacchettato. Inoltre non sono sicuro di cosa intendi per non avere il DB effettivo nel progetto, dato l'esempio che potresti fare "return new View (pastObj);" e non devi nemmeno modificare la vista (tranne la dichiarazione @model) – Brunner

risposta

10

È possibile utilizzare AutoMapper per rimuovere tutto il codice per la copia di valori tra modelli/entità.

Inoltre, considerare l'utilizzo di layout, attributi di Annotazione dati e modelli (con Html.DisplayFor e Html.EditorFor) per ridurre le visualizzazioni.

http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html

Si potrebbe indagare la possibilità di creare una classe base del controllore generico, che avrà il tipo di modello e Entity e conterrà logica comune per le operazioni CRUD, ma può essere un passo troppo lungo e ostacolare il tuo sviluppo più tardi.

+0

@Francis Tenere presente che l'automapper (o valueinjecter) deve essere utilizzato solo per la mappatura unidirezionale, non è consigliabile passare da un metodo post a un'entità. Funzionerebbe in alcuni casi, ma cade a pezzi in altri che si occupano di dire, il framework di entità come proprietà non ha il proprio flag "modificato", ma l'oggetto stesso non fa in modo che non vengano apportate modifiche al db. Solo una fyi per chiunque altro che legge. –

+0

@AdamTuliper - Ho usato AutoMapper con EF senza problemi. Tutto ciò che AutoMapper fa è chiamare i setter di proprietà, non è diverso dal fare manualmente nel codice. Potresti fare più luce sui problemi che hai vissuto? –

+0

Uso AutoMapper per la mappatura a 2 vie, ma non mappo più ViewModels direttamente alle entità. Le nostre entità ora hanno 'set interni 'protetti, per prevenire questo genere di cose. Invece, eseguo l'automap da un viewmodel a un oggetto comando, quindi eseguo l'aggiornamento effettivo delle proprietà dell'entità da un gestore comandi. Ho scoperto che l'automapping da viewmodels direttamente alle entità era troppo disordinato e soggetto a errori, senza alcun modo di applicare le regole aziendali a meno che non fossero nel livello dell'interfaccia utente. – danludwig

5

Penso che un controller dinamico potrebbe essere un passo troppo lungo - cosa succede quando si rende tutto generico e quindi una delle viste richiede qualcosa di più complesso di una semplice mappa nel database - estendi la tua vista generica controller 'per affrontare questo? Potrebbe terminare here.

Forse dovresti dare un'occhiata ad Automapper per rimuovere parte della natura ripetitiva di gestione dei tuoi modelli di visualizzazione.

http://automapper.codeplex.com/

+0

Sto dando un'occhiata all'automapper al momento. Potrebbero essere necessari alcuni giorni, ma tornerò dopo che ho fatto un po 'o un resarch per scegliere una risposta accettata. Nel frattempo grazie per la tua risposta. –

0

è possibile generare una modelli EditFor/Modello per oggetto, o ur classe base, qualunque essa sia, e poi basta fare un punto di vista, che accetterà il vostro modello, i relativi metadati (con l'aiuto di MVC metadataproviders), e costruisce dinamicamente i campi secondo.
this post copre hwo può essere fatto in MVC2, ma u può facilmente adattare questa tecnica per MVC 3, come pure ..
Im usando un tale modello in un progetto mi lavorando, dove ho poche decine di entità diverse , tutti condividono lo stesso aspetto grafico. Con l'aiuto del mio Metadataprovider, che mi aiuta anche a ottenere l'ordine dei campi e che dovrebbero essere visibili in ogni vista, ho costruito una vista esattamente per mostrare tutte le mie entità

+0

Grazie per la risposta. Sono in procinto di leggere quel link e sembra interessante. Anche se l'automapper sembra ancora di più. Posso vedere in una certa misura perché generalizzare il controller potrebbe essere un passo troppo lungo però. Ma ho bisogno di fare più ricerche. Grazie per la tua risposta. –

+0

tbh, non ho mai usato l'automapper, e questo naturalmente potrebbe essere la giusta soluzione per te .. buona fortuna :) – YavgenyP

0

Sono d'accordo con gli altri qui che avendo un singolo controller dinamico per gestire tutti i modelli e le visualizzazioni nell'applicazione sta andando troppo lontano. Esistono altri modi per ridurre la quantità di codice nei controller. Altre risposte qui hanno menzionato AutoMapper, che è un ottimo strumento. L'inventore di AutoMapper, Jimmy Bogard, ha anche uno great video here che include altre cose che puoi fare per ridurre la quantità di codice nei tuoi controller.

Per quanto riguarda le viste, è possibile avere già una vista che gestisce tutti i controller utilizzando EditorTemplates. È possibile creare un EditorTemplate personalizzato che utilizzerà la riflessione per esaminare il modello e rendere di conseguenza i campi di input. Tutto questo utilizza "metadati del modello", e c'è a good post on that by Brad Wilson here.

+0

Grazie per la risposta. Sto esaminando AutoMapper ora perché così tante persone lo hanno consigliato insieme al link al sito di Brad Wilson. Accetterò una risposta in pochi giorni dopo aver completato la mia ricerca. –

Problemi correlati