Domanda

Da questa domanda , sembra logico avere un il controller crea un ViewModel che riflette più accuratamente il modello che la vista sta tentando di visualizzare, ma sono curioso di alcune convenzioni (sono nuovo nel modello MVC, se non lo fosse già ovvio).

Fondamentalmente, ho avuto le seguenti domande:

  1. Normalmente mi piace avere una classe / file. Questo ha senso con un ViewModel se viene creato solo per trasferire dati da un controller a una vista?
  2. Se un ViewModel appartiene al suo stesso file e stai usando una struttura di directory / progetti per mantenere le cose separate, dove appartiene il file ViewModel ? Nella directory Controller ?

Per ora è praticamente tutto. Potrei avere qualche altra domanda in arrivo, ma questo mi ha infastidito nell'ultima ora e mi sembra di trovare una guida coerente altrove.

Modifica Guardando l'esempio app NerdDinner su CodePlex, sembra che i ViewModels facciano parte di Controller , ma mi fa ancora sentire a disagio che non si trovino nei propri file.

È stato utile?

Soluzione

Creo quello che chiamo un " ViewModel " per ogni vista. Li ho messi in una cartella chiamata ViewModels nel mio progetto Web MVC. Li chiamo dopo il controller e l'azione (o la vista) che rappresentano. Quindi, se devo passare i dati alla vista Registrazione sul controller di appartenenza, creo una classe MembershipSignUpViewModel.cs e li inserisco nella cartella ViewModels.

Quindi aggiungo le proprietà e i metodi necessari per facilitare il trasferimento dei dati dal controller alla vista. Uso Automapper per passare dal mio ViewModel al modello di dominio e viceversa, se necessario.

Funziona bene anche con ViewModel compositi che contengono proprietà del tipo di altri ViewModels. Ad esempio, se hai 5 widget nella pagina dell'indice nel controller di appartenenza e hai creato un ViewModel per ogni vista parziale - come passi i dati dall'azione Indice ai parziali? Si aggiunge una proprietà a MembershipIndexViewModel di tipo MyPartialViewModel e quando si esegue il rendering del parziale si passa in Model.MyPartialViewModel.

In questo modo è possibile regolare le proprietà ViewModel parziali senza modificare affatto la vista Indice. Passa ancora in Model.MyPartialViewModel, quindi c'è meno possibilità che tu debba passare attraverso l'intera catena di parziali per riparare qualcosa quando tutto ciò che stai facendo è aggiungere una proprietà al ViewModel parziale.

Aggiungerò anche lo spazio dei nomi " MyProject.Web.ViewModels " al web.config in modo da permettermi di fare riferimento a loro in qualsiasi vista senza mai aggiungere un'istruzione di importazione esplicita su ogni vista. Lo rende solo un po 'più pulito.

Altri suggerimenti

Separare le classi per categoria (Controller, ViewModels, Filtri ecc.) non ha senso.

Se vuoi scrivere il codice per la sezione Home del tuo sito web (/), crea una cartella denominata Home e inserisci lì HomeController, IndexViewModel, AboutViewModel, ecc. e tutte le classi correlate utilizzate dalle azioni Home.

Se hai classi condivise, come un ApplicationController, puoi metterlo alla radice del tuo progetto.

Perché separare le cose correlate (HomeController, IndexViewModel) e tenere insieme le cose che non hanno alcuna relazione (HomeController, AccountController)?


Ho scritto un post di blog su questo argomento.

Conservo le mie classi di applicazione in una sottocartella denominata " Core " (o una libreria di classi separata) e utilizzano gli stessi metodi dell'applicazione di esempio KIGG ma con alcune lievi modifiche a rendere le mie applicazioni più ASCIUTTE.

Creo una classe BaseViewData in / Core / ViewData / dove memorizzo le proprietà comuni del sito.

Successivamente, creo anche tutte le mie classi ViewData view nella stessa cartella che derivano da BaseViewData e hanno proprietà specifiche di visualizzazione.

Quindi creo un ApplicationController da cui derivano tutti i miei controller. ApplicationController ha un metodo GetViewData generico come segue:

protected T GetViewData<T>() where T : BaseViewData, new()
    {
        var viewData = new T
        {
           Property1 = "value1",
           Property2 = this.Method() // in the ApplicationController
        };
        return viewData;
    }

Infine, nella mia azione del controller faccio quanto segue per creare il mio modello ViewData

public ActionResult Index(int? id)
    {
        var viewData = this.GetViewData<PageViewData>();
        viewData.Page = this.DataContext.getPage(id); // ApplicationController
        ViewData.Model = viewData;
        return View();
    }

Penso che funzioni davvero bene e mantenga le tue opinioni in ordine e i tuoi controller magri.

Una classe ViewModel è lì per incapsulare più pezzi di dati rappresentati da istanze di classi in un oggetto facile da gestire che puoi passare alla tua vista.

Avrebbe senso avere le classi ViewModel nei propri file, nella propria directory. Nei miei progetti ho una sottocartella della cartella Modelli chiamata ViewModels. È qui che vivono i miei ViewModels (ad es. ProductViewModel.cs).

Non esiste un buon posto dove tenere i tuoi modelli. Puoi tenerli in un assieme separato se il progetto è grande e ci sono molti ViewModels (Data Transfer Objects). Inoltre puoi tenerli in una cartella separata del progetto del sito. Ad esempio, in Oxite sono inseriti nel progetto Oxite che contiene anche molte classi diverse. I controller in Oxite vengono spostati in un progetto separato e anche le visualizzazioni sono in un progetto separato.
In CodeCampServer ViewModels sono denominati * Form e vengono inseriti nel progetto UI nella cartella Models.
Nel progetto MvcPress vengono inseriti nel progetto Data, che contiene anche tutto il codice per lavorare con il database e altro ancora (ma non ho raccomandato questo approccio, è solo per un campione)
Quindi puoi vedere che ci sono molti punti di vista. Di solito mantengo i miei ViewModels (oggetti DTO) nel progetto del sito. Ma quando ho più di 10 modelli, preferisco spostarli in un gruppo separato. Di solito in questo caso sposto anche i controller per separare l'assemblaggio.
Un'altra domanda è come mappare facilmente tutti i dati dal modello al ViewModel. Suggerisco di dare un'occhiata alla libreria AutoMapper . Mi piace moltissimo, fa tutto il lavoro sporco per me.
E suggerisco anche di guardare il progetto SharpArchitecture . Fornisce un'ottima architettura per i progetti e contiene molti framework e linee guida interessanti e un'ottima community.

ecco uno snippet di codice delle mie migliori pratiche:

    public class UserController : Controller
    {
        private readonly IUserService userService;
        private readonly IBuilder<User, UserCreateInput> createBuilder;
        private readonly IBuilder<User, UserEditInput> editBuilder;

        public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
        {
            this.userService = userService;
            this.editBuilder = editBuilder;
            this.createBuilder = createBuilder;
        }

        public ActionResult Index(int? page)
        {
            return View(userService.GetPage(page ?? 1, 5));
        }

        public ActionResult Create()
        {
            return View(createBuilder.BuildInput(new User()));
        }

        [HttpPost]
        public ActionResult Create(UserCreateInput input)
        {
            if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");

            if (!ModelState.IsValid)
                return View(createBuilder.RebuildInput(input));

            userService.Create(createBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }

        public ActionResult Edit(long id)
        {
            return View(editBuilder.BuildInput(userService.GetFull(id)));
        }

        [HttpPost]
        public ActionResult Edit(UserEditInput input)
        {           
            if (!ModelState.IsValid)
                return View(editBuilder.RebuildInput(input));

            userService.Save(editBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }
}

Inseriamo tutti i nostri ViewModels nella cartella Models (tutta la nostra logica aziendale è in un progetto ServiceLayer separato)

Personalmente suggerirei se ViewModel è tutt'altro che banale, quindi utilizzare una classe separata.

Se si dispone di più di un modello di visualizzazione, è consigliabile partizionarlo in almeno una directory. se il modello di vista viene successivamente condiviso, lo spazio dei nomi implicito nella directory semplifica lo spostamento in un nuovo assembly.

Nel nostro caso abbiamo i Modelli insieme ai Controller in un progetto separato dalle Viste.

Come regola generale, abbiamo cercato di spostare ed evitare la maggior parte delle cose ViewData [" ... "] su ViewModel, quindi evitiamo casting e stringhe magiche, che è una buona cosa.

Il ViewModel contiene anche alcune proprietà comuni come le informazioni di impaginazione per gli elenchi o le informazioni di intestazione della pagina per disegnare breadcrumb e titoli. In questo momento la classe base contiene troppe informazioni secondo me e potremmo dividerle in tre pezzi, le informazioni più basilari e necessarie per il 99% delle pagine su un modello di vista di base, e quindi un modello per le liste e un modello per i moduli che contengono dati specifici per tali scenari ed ereditano da quello di base.

Infine, implementiamo un modello di visualizzazione per ogni entità per gestire le informazioni specifiche.

codice nel controller:

    [HttpGet]
        public ActionResult EntryEdit(int? entryId)
        {
            ViewData["BodyClass"] = "page-entryEdit";
            EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
            return View(viewMode);
        }

    [HttpPost]
    public ActionResult EntryEdit(Entry entry)
    {
        ViewData["BodyClass"] = "page-entryEdit";            

        #region save

        if (ModelState.IsValid)
        {
            if (EntryManager.Update(entry) == 1)
            {
                return RedirectToAction("EntryEditSuccess", "Dictionary");
            }
            else
            {
                return RedirectToAction("EntryEditFailed", "Dictionary");
            }
        }
        else
        {
            EntryEditViewModel viewModel = new EntryEditViewModel(entry);
            return View(viewModel);
        }

        #endregion
    }

codice nel modello di visualizzazione:

public class EntryEditViewModel
    {
        #region Private Variables for Properties

        private Entry _entry = new Entry();
        private StatusList _statusList = new StatusList();        

        #endregion

        #region Public Properties

        public Entry Entry
        {
            get { return _entry; }
            set { _entry = value; }
        }

        public StatusList StatusList
        {
            get { return _statusList; }
        }

        #endregion

        #region constructor(s)

        /// <summary>
        /// for Get action
        /// </summary>
        /// <param name="entryId"></param>
        public EntryEditViewModel(int? entryId)
        {
            this.Entry = EntryManager.GetDetail(entryId.Value);                 
        }

        /// <summary>
        /// for Post action
        /// </summary>
        /// <param name="entry"></param>
        public EntryEditViewModel(Entry entry)
        {
            this.Entry = entry;
        }

        #endregion       
    }

progetti:

  • DevJet.Web (il web ASP.NET MVC progetto)

  • DevJet.Web.App.Dictionary (a progetto separato della Biblioteca di classe)

    in questo progetto, ho creato alcune cartelle come: DAL, BLL, BO, VM (cartella per i modelli di visualizzazione)

Crea una classe base del modello di vista che ha comunemente richiesto proprietà come risultato dell'operazione e dati contestuali, puoi anche inserire dati e ruoli dell'utente corrente

class ViewModelBase 
{
  public bool HasError {get;set;} 
  public string ErrorMessage {get;set;}
  public List<string> UserRoles{get;set;}
}

Nella classe controller di base ha un metodo come PopulateViewModelBase () questo metodo riempirà i dati contestuali e i ruoli utente. HasError ed ErrorMessage, impostare queste proprietà in caso di eccezioni durante l'estrazione dei dati da service / db. Associare queste proprietà alla vista per mostrare l'errore. I ruoli utente possono essere utilizzati per mostrare la sezione nascondi nella vista in base ai ruoli.

Per popolare i modelli di vista in diverse azioni get, può essere reso coerente disponendo di controller di base con metodo astratto FillModel

class BaseController :BaseController 
{
   public PopulateViewModelBase(ViewModelBase model) 
{
   //fill up common data. 
}
abstract ViewModelBase FillModel();
}

Nei controller

class MyController :Controller 
{

 public ActionResult Index() 
{
   return View(FillModel()); 
}

ViewModelBase FillModel() 
{ 
    ViewModelBase  model=;
    string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString(); 
 try 
{ 
   switch(currentAction) 
{  
   case "Index": 
   model= GetCustomerData(); 
   break;
   // fill model logic for other actions 
}
}
catch(Exception ex) 
{
   model.HasError=true;
   model.ErrorMessage=ex.Message;
}
//fill common properties 
base.PopulateViewModelBase(model);
return model;
}
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top