Modifica: ho aggiornato questa risposta sul mio blog:
http://www.samwheat.com/post/The-function-of-ViewModels-in-MVC-web-development
La mia risposta è un po 'lunga, ma penso che sia importante confrontare i modelli di vista con altri tipi di modelli comunemente usati per capire perché sono diversi e perché sono necessari.
Riassumendo e rispondere direttamente alla domanda che viene posta:
In generale, un modello di vista è un oggetto che contiene tutte le proprietà e i metodi necessari per eseguire il rendering di una vista. Le proprietà del modello di visualizzazione sono spesso correlate a oggetti dati come clienti e ordini e inoltre contengono anche proprietà correlate alla pagina o all'applicazione stessa come nome utente, nome dell'applicazione, ecc. I modelli di visualizzazione forniscono un oggetto conveniente da passare a un motore di rendering a crea una pagina html. Uno dei molti motivi per utilizzare un modello di visualizzazione è che i modelli di visualizzazione forniscono un modo per testare l'unità di determinate attività di presentazione come la gestione dell'input dell'utente, la convalida dei dati, il recupero dei dati per la visualizzazione, ecc.
Ecco un confronto tra i modelli Entity (a.ka. a.ka. a.ka. models), Presentation Models e View Models.
Oggetti di trasferimento dati noti anche come "Modello"
Un Data Transfer Object (DTO) è una classe con proprietà che corrispondono a uno schema di tabella in un database. I DTO sono nominati per il loro uso comune per il trasferimento di dati da e verso un archivio dati.
Caratteristiche dei DTO:
• Sono oggetti business: la loro definizione dipende dai dati dell'applicazione.
• Solitamente contengono solo proprietà - nessun codice.
• Utilizzato principalmente per il trasporto di dati da e verso un database.
• Le proprietà corrispondono esattamente o strettamente ai campi su una tabella specifica in un archivio dati.
Le tabelle del database sono normalmente normalizzate, pertanto anche i DTO sono normalmente normalizzati. Ciò li rende di scarsa utilità per la presentazione dei dati. Tuttavia, per alcune semplici strutture dati spesso fanno abbastanza bene.
Ecco due esempi di come potrebbero apparire i DTO:
public class Customer
{
public int ID { get; set; }
public string CustomerName { get; set; }
}
public class Order
{
public int ID { get; set; }
public int CustomerID { get; set; }
public DateTime OrderDate { get; set; }
public Decimal OrderAmount { get; set; }
}
Modelli di presentazione
Un modello di presentazione è una classe di utilità utilizzata per il rendering dei dati su una schermata o un report. I modelli di presentazione sono in genere utilizzati per modellare strutture dati complesse composte da dati provenienti da più DTO. I modelli di presentazione spesso rappresentano una vista denormalizzata dei dati.
Caratteristiche dei modelli di presentazione:
• Sono oggetti business: la loro definizione dipende dai dati dell'applicazione.
• Contengono principalmente proprietà. Il codice è in genere limitato alla formattazione dei dati o alla conversione in o da un DTO. I modelli di presentazione non devono contenere la logica aziendale.
• Spesso presentano una vista denormalizzata dei dati. Cioè, spesso combinano proprietà di più DTO.
• Spesso contengono proprietà di un tipo di base diverso rispetto a un DTO. Ad esempio, gli importi in dollari possono essere rappresentati come stringhe in modo che possano contenere virgole e un simbolo di valuta.
• Spesso definito dal modo in cui vengono utilizzati e dalle loro caratteristiche dell'oggetto. In altre parole, un semplice DTO utilizzato come modello di supporto per il rendering di una griglia è in realtà anche un modello di presentazione nel contesto di quella griglia.
I modelli di presentazione vengono utilizzati “secondo necessità” e “dove necessario” (mentre i DTO sono generalmente legati allo schema del database). Un modello di presentazione può essere utilizzato per modellare i dati di un'intera pagina, una griglia su una pagina o un menu a discesa su una griglia su una pagina. I modelli di presentazione contengono spesso proprietà che sono altri modelli di presentazione. I modelli di presentazione sono spesso costruiti per uno scopo monouso, come il rendering di una griglia specifica su una singola pagina.
Un modello di presentazione di esempio:
public class PresentationOrder
{
public int OrderID { get; set; }
public DateTime OrderDate { get; set; }
public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
public string CustomerName { get; set; }
public Decimal OrderAmount { get; set; }
public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}
Visualizza i modelli
Un modello di vista è simile a un modello di presentazione in quanto è una classe di supporto per il rendering di una vista. Tuttavia è molto diverso da un modello di presentazione o da un DTO nel modo in cui è costruito. I modelli di visualizzazione contengono spesso le stesse proprietà dei modelli di presentazione e dei DTO e per questo motivo sono spesso confusi l'uno per l'altro.
Caratteristiche dei modelli di visualizzazione:
• Sono la singola fonte di dati utilizzata per il rendering di una pagina o di una schermata. Di solito ciò significa che un modello di vista esporrà tutte le proprietà necessarie per il rendering corretto di qualsiasi controllo sulla pagina. Rendere il modello della vista come unica fonte di dati per la vista migliora notevolmente la sua capacità e il suo valore per i test unitari.
• Sono oggetti compositi che contengono proprietà costituite da dati dell'applicazione e proprietà utilizzate dal codice dell'applicazione. Questa caratteristica è cruciale quando si progetta il modello di visualizzazione per la riusabilità ed è discussa negli esempi seguenti.
• Contiene il codice dell'applicazione. I modelli di visualizzazione in genere contengono metodi chiamati durante il rendering e quando l'utente interagisce con la pagina. Questo codice si riferisce in genere alla gestione degli eventi, all'animazione, alla visibilità dei controlli, allo stile, ecc.
• Contiene codice che chiama i servizi aziendali allo scopo di recuperare dati o inviarli a un server di database. Questo codice viene spesso erroneamente inserito in un controller. La chiamata ai servizi aziendali da un controller di solito limita l'utilità del modello di visualizzazione per i test unitari. Per essere chiari, gli stessi modelli di visualizzazione non dovrebbero contenere la logica aziendale ma dovrebbero effettuare chiamate ai servizi che contengono la logica aziendale.
• Spesso contengono proprietà che sono altri modelli di visualizzazione per altre pagine o schermate.
• Sono scritti "per pagina" o "per schermo". Un modello di visualizzazione univoco viene in genere scritto per ogni pagina o schermata in un'applicazione.
• Di solito derivano da una classe di base poiché la maggior parte delle pagine e schermate condividono proprietà comuni.
Visualizza la composizione del modello
Come affermato in precedenza, i modelli di visualizzazione sono oggetti compositi in quanto combinano le proprietà dell'applicazione e le proprietà dei dati aziendali su un singolo oggetto. Esempi di proprietà dell'applicazione comunemente utilizzate che vengono utilizzate nei modelli di visualizzazione sono:
• Proprietà utilizzate per visualizzare lo stato dell'applicazione come messaggi di errore, nome utente, stato, ecc.
• Proprietà utilizzate per formattare, visualizzare, stilizzare o animare i controlli.
• Proprietà utilizzate per l'associazione di dati come oggetti elenco e proprietà che contengono dati intermedi immessi dall'utente.
Gli esempi seguenti mostrano perché la natura composita dei modelli di vista è importante e come possiamo costruire al meglio un modello di vista così efficiente e riutilizzabile.
Supponiamo che stiamo scrivendo un'applicazione web. Uno dei requisiti del design dell'applicazione è che il titolo della pagina, il nome utente e il nome dell'applicazione devono essere visualizzati su ogni pagina. Se vogliamo creare una pagina per visualizzare un oggetto ordine di presentazione, possiamo modificare il modello di presentazione come segue:
public class PresentationOrder
{
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
public int OrderID { get; set; }
public DateTime OrderDate { get; set; }
public string PrettyDate { get { return OrderDate.ToShortDateString(); } }
public string CustomerName { get; set; }
public Decimal OrderAmount { get; set; }
public string PrettyAmount { get { return string.Format("{0:C}", OrderAmount); } }
}
Questo design potrebbe funzionare ... ma se vogliamo creare una pagina che visualizzerà un elenco di ordini? Le proprietà PageTitle, UserName e ApplicationName verranno ripetute e diventeranno poco maneggevoli. Inoltre, se vogliamo definire una logica a livello di pagina nel costruttore della classe? Non possiamo più farlo se creiamo un'istanza per ogni ordine che verrà visualizzato.
Composizione su eredità
Ecco un modo in cui potremmo ricodificare il modello di presentazione dell'ordine in modo che diventi un vero modello di visualizzazione e sarà utile per visualizzare un singolo oggetto PresentationOrder o una raccolta di oggetti PresentationOrder:
public class PresentationOrderVM
{
// Application properties
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
// Business properties
public PresentationOrder Order { get; set; }
}
public class PresentationOrderVM
{
// Application properties
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
// Business properties
public List<PresentationOrder> Orders { get; set; }
}
Osservando le due classi precedenti possiamo vedere che un modo di pensare a un modello di vista è che è un modello di presentazione che contiene un altro modello di presentazione come proprietà. Il modello di presentazione di livello superiore (ad es. Modello di visualizzazione) contiene proprietà rilevanti per la pagina o l'applicazione mentre il modello di presentazione (proprietà) contiene proprietà rilevanti per i dati dell'applicazione.
Possiamo fare un ulteriore passo avanti nel nostro design e creare una classe del modello di vista di base che può essere utilizzata non solo per PresentationOrders, ma anche per qualsiasi altra classe:
public class BaseViewModel
{
// Application properties
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
}
Ora possiamo semplificare il nostro PresentationOrderVM in questo modo:
public class PresentationOrderVM : BaseViewModel
{
// Business properties
public PresentationOrder Order { get; set; }
}
public class PresentationOrderVM : BaseViewModel
{
// Business properties
public List<PresentationOrder> Orders { get; set; }
}
Possiamo rendere il nostro BaseViewModel ancora più riutilizzabile rendendolo generico:
public class BaseViewModel<T>
{
// Application properties
public string PageTitle { get; set; }
public string UserName { get; set; }
public string ApplicationName { get; set; }
// Business property
public T BusinessObject { get; set; }
}
Ora le nostre implementazioni sono semplici:
public class PresentationOrderVM : BaseViewModel<PresentationOrder>
{
// done!
}
public class PresentationOrderVM : BaseViewModel<List<PresentationOrder>>
{
// done!
}