Qu'est-ce que ViewModel dans MVC?

429

Je suis nouveau sur ASP.NET MVC. J'ai un problème avec la compréhension de l'objectif d'un ViewModel.

Qu'est-ce qu'un ViewModel et pourquoi avons-nous besoin d'un ViewModel pour une application ASP.NET MVC?

Si je reçois un bon exemple de son fonctionnement et de ses explications, ce serait mieux.

unique
la source
4
Ce message est ce que vous recherchez - "Qu'est-ce qu'un ViewModel ASP.NET MVC?"
Yusubov
6
Cet article a fière allure: rachelappel.com/…
Andrew
doublon possible d' In MVC, qu'est-ce qu'un ViewModel?
rogerdeuce

Réponses:

607

A view modelreprésente les données que vous souhaitez afficher sur votre vue / page, qu'elles soient utilisées pour du texte statique ou pour des valeurs d'entrée (comme des zones de texte et des listes déroulantes) qui peuvent être ajoutées à la base de données (ou modifiées). C'est quelque chose de différent du vôtre domain model. C'est un modèle pour la vue.

Disons que vous avez un Employee classe qui représente votre modèle de domaine d'employé et qui contient les propriétés suivantes (identifiant unique, prénom, nom et date de création):

public class Employee : IEntity
{
     public int Id { get; set; }

     public string FirstName { get; set; }

     public string LastName { get; set; }

     public DateTime DateCreated { get; set; }
}

Les modèles de vue diffèrent des modèles de domaine dans la mesure où les modèles de vue contiennent uniquement les données (représentées par des propriétés) que vous souhaitez utiliser dans votre vue. Par exemple, supposons que vous souhaitiez ajouter un nouvel enregistrement d'employé, votre modèle d'affichage pourrait ressembler à ceci:

public class CreateEmployeeViewModel
{
     public string FirstName { get; set; }

     public string LastName { get; set; }
}

Comme vous pouvez le voir, il ne contient que deux des propriétés. Ces deux propriétés se trouvent également dans le modèle de domaine des employés. Pourquoi demandez-vous cela? Idpeut ne pas être défini à partir de la vue, il peut être généré automatiquement par la table Employé. Et DateCreatedpeut également être défini dans la procédure stockée ou dans la couche de service de votre application. Donc Idet DateCreatedne sont pas nécessaires dans le modèle de vue. Vous souhaiterez peut-être afficher ces deux propriétés lorsque vous afficherez les détails d'un employé (un employé qui a déjà été capturé) sous forme de texte statique.

Lors du chargement de la vue / page, la méthode d'action de création dans votre contrôleur d'employé créera une instance de ce modèle de vue, remplira tous les champs si nécessaire, puis passera ce modèle de vue à la vue / page:

public class EmployeeController : Controller
{
     private readonly IEmployeeService employeeService;

     public EmployeeController(IEmployeeService employeeService)
     {
          this.employeeService = employeeService;
     }

     public ActionResult Create()
     {
          CreateEmployeeViewModel model = new CreateEmployeeViewModel();

          return View(model);
     }

     public ActionResult Create(CreateEmployeeViewModel model)
     {
          // Do what ever needs to be done before adding the employee to the database
     }
}

Votre vue / page pourrait ressembler à ceci (en supposant que vous utilisez ASP.NET MVCet le Razormoteur de vue):

@model MyProject.Web.ViewModels.CreateEmployeeViewModel

<table>
     <tr>
          <td><b>First Name:</b></td>
          <td>@Html.TextBoxFor(m => m.FirstName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.FirstName)
          </td>
     </tr>
     <tr>
          <td><b>Last Name:</b></td>
          <td>@Html.TextBoxFor(m => m.LastName, new { maxlength = "50", size = "50" })
              @Html.ValidationMessageFor(m => m.LastName)
          </td>
     </tr>
</table>

La validation ne se ferait donc que sur FirstNameet LastName. En utilisant FluentValidation, vous pouvez avoir une validation comme celle-ci:

public class CreateEmployeeViewModelValidator : AbstractValidator<CreateEmployeeViewModel>
{
     public CreateEmployeeViewModelValidator()
     {
          RuleFor(m => m.FirstName)
               .NotEmpty()
               .WithMessage("First name required")
               .Length(1, 50)
               .WithMessage("First name must not be greater than 50 characters");

          RuleFor(m => m.LastName)
               .NotEmpty()
               .WithMessage("Last name required")
               .Length(1, 50)
               .WithMessage("Last name must not be greater than 50 characters");
     }
}

Et avec les annotations de données, cela pourrait ressembler à ceci:

public class CreateEmployeeViewModel : ViewModelBase
{
    [Display(Name = "First Name")]
    [Required(ErrorMessage = "First name required")]
    public string FirstName { get; set; }

    [Display(Name = "Last Name")]
    [Required(ErrorMessage = "Last name required")]
    public string LastName { get; set; }
}

L'essentiel à retenir est que le modèle de vue représente uniquement les données que vous souhaitez utiliser , rien d'autre. Vous pouvez imaginer tout le code et la validation inutiles si vous avez un modèle de domaine avec 30 propriétés et que vous ne souhaitez mettre à jour qu'une seule valeur. Dans ce scénario, vous n'auriez qu'une seule valeur / propriété dans le modèle de vue et pas toutes les propriétés qui se trouvent dans l'objet de domaine.

Un modèle de vue peut non seulement contenir des données d'une table de base de données. Il peut combiner des données d'une autre table. Prenez mon exemple ci-dessus sur l'ajout d'un nouvel enregistrement d'employé. Outre l'ajout des noms et prénoms, vous pouvez également ajouter le service de l'employé. Cette liste de départements proviendra de votre Departmentstable. Alors maintenant, vous avez les données des tables Employeeset Departmentsdans un modèle de vue. Vous n'aurez alors qu'à ajouter les deux propriétés suivantes à votre modèle de vue et à le remplir avec des données:

public int DepartmentId { get; set; }

public IEnumerable<Department> Departments { get; set; }

Lors de la modification des données des employés (un employé qui a déjà été ajouté à la base de données), cela ne diffère pas beaucoup de mon exemple ci-dessus. Créez un modèle de vue, appelez-le par exemple EditEmployeeViewModel. Ne disposez que des données que vous souhaitez modifier dans ce modèle de vue, comme le prénom et le nom. Modifiez les données et cliquez sur le bouton Soumettre. Je ne m'inquiéterais pas trop du Idchamp car la Idvaleur sera probablement dans l'URL, par exemple:

http://www.yourwebsite.com/Employee/Edit/3

Prenez-le Idet transmettez-le à votre couche de référentiel, avec vos valeurs de prénom et nom.

Lors de la suppression d'un enregistrement, je suis normalement le même chemin qu'avec le modèle de vue d'édition. J'aurais aussi une URL, par exemple:

http://www.yourwebsite.com/Employee/Delete/3

Lorsque la vue se charge pour la première fois, j'obtiendrais les données de l'employé de la base de données en utilisant le Id3. J'afficherais alors simplement du texte statique sur ma vue / page afin que l'utilisateur puisse voir quel employé est supprimé. Lorsque l'utilisateur clique sur le bouton Supprimer, j'utilise simplement la Idvaleur 3 et la transmets à ma couche de référentiel. Vous avez seulement besoin de Idpour supprimer un enregistrement de la table.

Autre point, vous n'avez pas vraiment besoin d'un modèle de vue pour chaque action. S'il s'agit de données simples, il serait bon de les utiliser uniquement EmployeeViewModel. S'il s'agit de vues / pages complexes et qu'elles diffèrent les unes des autres, je vous suggère d'utiliser des modèles de vue distincts pour chacune.

J'espère que cela dissipe toute confusion que vous aviez sur les modèles de vue et les modèles de domaine.

Brendan Vogt
la source
5
@Kenny: Alors montrez-le :) Ce que j'essayais de dire, c'est que disons que vous avez un modèle de domaine avec 50 propriétés et que votre vue n'a besoin que d'en afficher 5, alors il ne sert à rien d'envoyer les 50 propriétés juste pour en afficher 5.
Brendan Vogt
5
@BrendanVogt - vous avez bien expliqué cela, mais je ne comprends pas le coût de "l'envoi des 50 propriétés". Autre code a déjà créé un objet modèle, avec les 50 propriétés, et il ne semble pas utile de maintenir une autre classe juste pour ne pas envoyer 45 propriétés - surtout si vous pourriez vouloir envoyer une de ces 45 propriétés dans l'avenir.
Kenny Evitt
5
@BrendanVogt - Je pense que la réponse de LukLed m'aide peut-être à comprendre pourquoi ceux-ci pourraient être utiles, en particulier qu'un ViewModel (peut) "... combiner des valeurs provenant de différentes entités de base de données" [où je suppose que la phrase est tout aussi vraie " entités de base de données "à remplacer par" Objets de modèle "]. Mais encore, à quels problèmes spécifiques les ViewModels étaient-ils censés répondre? Avez-vous des liens? Je n'ai rien trouvé moi-même. [Et je m'excuse si j'ai l'air de vous
harceler
1
Je viens d'entendre quelqu'un dire que ViewModels est un bon moyen d'envoyer plusieurs collections (ou propriétés de modèle croisé) dans une seule vue sans avoir à les remplir dans viewBag. Ça a du sens pour moi.
Ayyash
3
Je suis désolé d'être critique, mais cette réponse est malheureusement incomplète. Définir un modèle de vue comme étant uniquement ce dont vous avez besoin d'afficher sur votre page, c'est comme demander "Qu'est-ce qu'une voiture?" et recevoir une réponse "Ce n'est pas un avion". C'est vrai mais pas très utile. La définition la plus correcte d'une machine virtuelle est «Tout ce dont vous avez besoin pour afficher votre page». Si vous lisez vers le bas, j'ai identifié les composants dont vous avez besoin pour construire vos machines virtuelles correctement et facilement, dans de nombreux cas en tirant parti de vos modèles de domaine et modèles de présentation existants.
Sam
133

Le modèle de vue est une classe qui représente le modèle de données utilisé dans une vue spécifique. Nous pourrions utiliser cette classe comme modèle pour une page de connexion:

public class LoginPageVM
{
    [Required(ErrorMessage = "Are you really trying to login without entering username?")]
    [DisplayName("Username/e-mail")]
    public string UserName { get; set; }
    [Required(ErrorMessage = "Please enter password:)")]
    [DisplayName("Password")]
    public string Password { get; set; }
    [DisplayName("Stay logged in when browser is closed")]
    public bool RememberMe { get; set; }
}

En utilisant ce modèle de vue, vous pouvez définir la vue (moteur de vue Razor):

@model CamelTrap.Models.ViewModels.LoginPageVM

@using (Html.BeginForm()) {
    @Html.EditorFor(m => m);
    <input type="submit" value="Save" class="submit" />
}

Et actions:

[HttpGet]
public ActionResult LoginPage()
{
    return View();
}

[HttpPost]
public ActionResult LoginPage(LoginPageVM model)
{
    ...code to login user to application...
    return View(model);
}

Ce qui produit ce résultat (l'écran est pris après l'envoi du formulaire, avec des messages de validation):

Comme vous pouvez le voir, un modèle de vue a plusieurs rôles:

  • Les modèles de vue documentent une vue en ne comprenant que des champs, qui sont représentés dans la vue.
  • Les modèles de vue peuvent contenir des règles de validation spécifiques à l'aide d'annotations de données ou d'IDataErrorInfo.
  • Voir le modèle définit comment une vue devrait ressembler (pour LabelFor, EditorFor, DisplayForaides).
  • Les modèles de vue peuvent combiner les valeurs de différentes entités de base de données.
  • Vous pouvez spécifier facilement des modèles d'affichage pour les modèles de vue et les réutiliser à de nombreux endroits à l'aide des assistants DisplayFor ou EditorFor.

Un autre exemple d'un modèle de vue et de sa récupération: Nous voulons afficher les données utilisateur de base, ses privilèges et le nom des utilisateurs. Nous créons un modèle de vue spécial, qui contient uniquement les champs obligatoires. Nous récupérons les données de différentes entités de la base de données, mais la vue ne connaît que la classe du modèle de vue:

public class UserVM {
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsAdministrator { get; set; }
    public string MothersName { get; set; }
}

Récupération:

var user = db.userRepository.GetUser(id);

var model = new UserVM() {
   ID = user.ID,
   FirstName = user.FirstName,
   LastName = user.LastName,
   IsAdministrator = user.Proviledges.IsAdministrator,
   MothersName = user.Mother.FirstName + " " + user.Mother.LastName
} 
LukLed
la source
I thin user.Mother.FirstName + "" + user.Mother.LastName doit être fait dans View Model End. Toute la logique doit être effectuée à la fin du modèle d'affichage.
Kurkula
3
@Chandana: Je pense qu'une simple concaténation peut être effectuée dans le modèle de vue. Il n'y a aucune raison d'exposer deux champs s'ils sont destinés à être présentés ensemble.
LukLed
82

Edit: j'ai mis à jour cette réponse sur mon blog:

http://www.samwheat.com/post/The-function-of-ViewModels-in-MVC-web-development

Ma réponse est un peu longue mais je pense qu'il est important de comparer les modèles de vue à d'autres types de modèles couramment utilisés pour comprendre pourquoi ils sont différents et pourquoi ils sont nécessaires.

Pour résumer et répondre directement à la question posée:

De manière générale, un modèle de vue est un objet qui contient toutes les propriétés et méthodes nécessaires pour rendre une vue. Les propriétés du modèle de vue sont souvent liées à des objets de données tels que les clients et les commandes et, en outre, elles contiennent également des propriétés liées à la page ou à l'application elle-même, telles que le nom d'utilisateur, le nom de l'application, etc. Les modèles de vue fournissent un objet pratique à transmettre à un moteur de rendu. créer une page html. L'une des nombreuses raisons d'utiliser un modèle de vue est que les modèles de vue fournissent un moyen de tester à l'unité certaines tâches de présentation telles que la gestion des entrées utilisateur, la validation des données, la récupération des données pour l'affichage, etc.

Voici une comparaison des modèles Entity (a.ka. DTO's a.ka. models), Presentation Models et View Models.

Objets de transfert de données alias «modèle»

Un objet de transfert de données (DTO) est une classe dont les propriétés correspondent à un schéma de table dans une base de données. Les DTO sont nommés pour leur utilisation courante pour la navette de données vers et depuis un magasin de données.
Caractéristiques des DTO:

• Sont des objets métier - leur définition dépend des données d'application.

• Ne contiennent généralement que des propriétés - pas de code.

• Principalement utilisé pour transporter des données vers et depuis une base de données.

• Les propriétés correspondent exactement ou étroitement aux champs d'une table spécifique dans un magasin de données.

Les tables de base de données sont généralement normalisées, les DTO sont donc également normalisés. Cela les rend d'une utilité limitée pour la présentation des données. Cependant, pour certaines structures de données simples, elles fonctionnent souvent assez bien.

Voici deux exemples de ce à quoi pourraient ressembler les 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; }
}

Modèles de présentation

Un modèle de présentation est une classe utilitaire utilisée pour afficher des données sur un écran ou un rapport. Les modèles de présentation sont généralement utilisés pour modéliser des structures de données complexes composées de données provenant de plusieurs DTO. Les modèles de présentation représentent souvent une vue dénormalisée des données.

Caractéristiques des modèles de présentation:

• Sont des objets métier - leur définition dépend des données d'application.

• Contiennent principalement des propriétés. Le code est généralement limité au formatage des données ou à la conversion vers ou depuis un DTO. Les modèles de présentation ne doivent pas contenir de logique métier.

• Présentent souvent une vue dénormalisée des données. Autrement dit, ils combinent souvent les propriétés de plusieurs DTO.

• Contiennent souvent des propriétés d'un type de base différent de celui d'un DTO. Par exemple, les montants en dollars peuvent être représentés sous forme de chaînes afin qu'ils puissent contenir des virgules et un symbole monétaire.

• Souvent définis par la façon dont ils sont utilisés ainsi que par les caractéristiques de leur objet. En d'autres termes, un DTO simple utilisé comme modèle de support pour le rendu d'une grille est en fait également un modèle de présentation dans le contexte de cette grille.

Les modèles de présentation sont utilisés «selon les besoins» et «si nécessaire» (alors que les DTO sont généralement liés au schéma de la base de données). Un modèle de présentation peut être utilisé pour modéliser des données pour une page entière, une grille sur une page ou une liste déroulante sur une grille sur une page. Les modèles de présentation contiennent souvent des propriétés qui sont d'autres modèles de présentation. Les modèles de présentation sont souvent construits dans un but à usage unique, comme rendre une grille spécifique sur une seule page.

Un exemple de modèle de présentation:

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); } }
}

Voir les modèles

Un modèle de vue est similaire à un modèle de présentation dans la mesure où il s'agit d'une classe de support pour le rendu d'une vue. Cependant, il est très différent d'un modèle de présentation ou d'un DTO dans la façon dont il est construit. Les modèles de vue contiennent souvent les mêmes propriétés que les modèles de présentation et les DTO et, pour cette raison, ils sont souvent confondus l'un avec l'autre.

Caractéristiques des modèles de vue:

• Sont la seule source de données utilisée pour rendre une page ou un écran. Cela signifie généralement qu'un modèle de vue exposera toutes les propriétés dont tout contrôle de la page aura besoin pour s'afficher correctement. Faire du modèle de vue la source unique de données pour la vue améliore considérablement sa capacité et sa valeur pour les tests unitaires.

• Sont des objets composites qui contiennent des propriétés constituées de données d'application ainsi que des propriétés utilisées par le code d'application. Cette caractéristique est cruciale lors de la conception du modèle de vue pour la réutilisabilité et est discutée dans les exemples ci-dessous.

• Contient le code d'application. Les modèles de vue contiennent généralement des méthodes qui sont appelées pendant le rendu et lorsque l'utilisateur interagit avec la page. Ce code concerne généralement la gestion des événements, l'animation, la visibilité des contrôles, le style, etc.

• Contenir du code qui appelle des services commerciaux dans le but de récupérer des données ou de les envoyer à un serveur de base de données. Ce code est souvent placé par erreur dans un contrôleur. L'appel de services métier à partir d'un contrôleur limite généralement l'utilité du modèle d'affichage pour les tests unitaires. Pour être clair, les modèles de vue eux-mêmes ne doivent pas contenir de logique métier mais doivent appeler des services qui contiennent une logique métier.

• Contiennent souvent des propriétés qui sont d'autres modèles de vue pour d'autres pages ou écrans.

• Sont écrits «par page» ou «par écran». Un modèle de vue unique est généralement écrit pour chaque page ou écran d'une application.

• Dérivent généralement d'une classe de base car la plupart des pages et écrans partagent des propriétés communes.

Voir la composition du modèle

Comme indiqué précédemment, les modèles de vue sont des objets composites dans la mesure où ils combinent les propriétés d'application et les propriétés de données métier sur un seul objet. Voici des exemples de propriétés d'application couramment utilisées sur les modèles de vue:

• Propriétés utilisées pour afficher l'état de l'application, telles que les messages d'erreur, le nom d'utilisateur, l'état, etc.

• Propriétés utilisées pour formater, afficher, styliser ou animer des contrôles.

• Propriétés utilisées pour la liaison de données, telles que les objets de liste et les propriétés qui contiennent des données intermédiaires entrées par l'utilisateur.

Les exemples suivants montrent pourquoi la nature composite des modèles de vue est importante et comment nous pouvons construire au mieux un modèle de vue efficace et réutilisable.

Supposons que nous écrivons une application Web. L'une des exigences de la conception de l'application est que le titre de la page, le nom d'utilisateur et le nom de l'application doivent être affichés sur chaque page. Si nous voulons créer une page pour afficher un objet d'ordre de présentation, nous pouvons modifier le modèle de présentation comme suit:

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); } }
}

Cette conception pourrait fonctionner… mais que se passe-t-il si nous voulons créer une page qui affichera une liste de commandes? Les propriétés PageTitle, UserName et ApplicationName seront répétées et deviendront difficiles à utiliser. Et si nous voulons définir une logique de niveau page dans le constructeur de la classe? Nous ne pouvons plus le faire si nous créons une instance pour chaque commande qui sera affichée.

Composition sur héritage

Voici une façon dont nous pourrions re-factoriser le modèle de présentation des commandes de sorte qu'il devienne un véritable modèle de vue et sera utile pour afficher un seul objet PresentationOrder ou une collection d'objets 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; }
}

En regardant les deux classes ci-dessus, nous pouvons voir qu'une façon de penser à un modèle de vue est qu'il s'agit d'un modèle de présentation qui contient un autre modèle de présentation en tant que propriété. Le modèle de présentation de niveau supérieur (c'est-à-dire le modèle de vue) contient des propriétés pertinentes pour la page ou l'application tandis que le modèle de présentation (propriété) contient des propriétés pertinentes pour les données d'application.

Nous pouvons aller plus loin dans notre conception et créer une classe de modèle de vue de base qui peut être utilisée non seulement pour PresentationOrders, mais également pour toute autre classe:

public class BaseViewModel
{
    // Application properties
    public string PageTitle { get; set; }
    public string UserName { get; set; }
    public string ApplicationName { get; set; }
}

Maintenant, nous pouvons simplifier notre PresentationOrderVM comme ceci:

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public PresentationOrder Order { get; set; }
}

public class PresentationOrderVM : BaseViewModel
{
    // Business properties
    public List<PresentationOrder> Orders { get; set; }
}

Nous pouvons rendre notre BaseViewModel encore plus réutilisable en le rendant générique:

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; }
}

Maintenant, nos implémentations sont sans effort:

public class PresentationOrderVM : BaseViewModel<PresentationOrder>
{
    // done!
}

public class PresentationOrderVM : BaseViewModel<List<PresentationOrder>>
{
    // done!
}
Sam
la source
2
Merci Sam !! cela m'a aidé à saisir pleinement l'entité à multiples facettes qui est un: View-Model. Je suis un étudiant qui vient d'apprendre l'architecture MVC, et cela a clarifié un tas de fonctionnalités capables qui sont exposées au développeur. Si je le pouvais, je mettrais une étoile à côté de votre réponse.
Chef_Code
1
@Sam 'Les modèles de vue contiennent souvent les mêmes propriétés que les modèles de présentation et les DTO et pour cette raison, ils sont souvent confondus l'un avec l'autre.' Cela signifie-t-il qu'ils sont couramment utilisés à la place des modèles de présentation, ou sont-ils censés contenir les modèles / dtos de présentation?
Alexander Derck
2
@AlexanderDerck Ils sont utilisés à des fins différentes. Ils sont confondus l'un pour l'autre (par erreur). Non, vous n'utiliserez généralement pas de modèle pres à la place d'un modèle de vue. Beaucoup plus courant est que la VM "contient" le modèle de présentation, c'estMyViewModel<MyPresModel>
Sam
2
@Sam En supposant que les objets du modèle sont des objets vivants, par exemple des modèles nibernates. Donc, en ayant BusinessObject, ne sommes-nous pas en train d'exposer des modèles / objets vivants directement à la vue? c'est-à-dire que l'objet métier peut être utilisé pour modifier directement l'état de la base de données? Et les modèles de vues imbriquées? Cela nécessiterait plusieurs propriétés d'objet métier, non?
Muhammad Ali
22

Si vous avez des propriétés spécifiques à la vue et non liées au magasin de base de données / service / données, il est recommandé d'utiliser ViewModels. Supposons que vous souhaitiez laisser une case à cocher basée sur un champ DB (ou deux), mais le champ DB lui-même n'est pas un booléen. Bien qu'il soit possible de créer ces propriétés dans le modèle lui-même et de le garder caché de la liaison aux données, vous ne souhaiterez peut-être pas encombrer le modèle en fonction de la quantité de ces champs et transactions.

S'il y a trop peu de données et / ou de transformations spécifiques à la vue, vous pouvez utiliser le modèle lui-même

fozylet
la source
19

Je n'ai pas lu tous les articles mais chaque réponse semble manquer d'un concept qui m'a vraiment aidé à "comprendre" ...

Si un modèle s'apparente à une table de base de données , un ViewModel s'apparente à une vue de base de données : une vue renvoie généralement de petites quantités de données d'une table ou des ensembles complexes de données de plusieurs tables (jointures).

Je me retrouve à utiliser ViewModels pour transmettre des informations dans une vue / formulaire, puis à transférer ces données dans un modèle valide lorsque le formulaire est renvoyé au contrôleur - également très pratique pour stocker des listes (IEnumerable).

halfacreSal
la source
11

MVC n'a pas de modèle de vue: il a un modèle, une vue et un contrôleur. Un viewmodel fait partie de MVVM (Model-View-Viewmodel). MVVM est dérivé du modèle de présentation et est popularisé dans WPF. Il devrait également y avoir un modèle dans MVVM, mais la plupart des gens ratent complètement le point de ce modèle et ils n'auront qu'une vue et un modèle de vue. Le modèle dans MVC est similaire au modèle dans MVVM.

Dans MVC, le processus est divisé en 3 responsabilités différentes:

  • View est responsable de la présentation des données à l'utilisateur
  • Un contrôleur est responsable du flux de pages
  • Un modèle est responsable de la logique métier

MVC n'est pas très adapté aux applications Web. Il s'agit d'un modèle introduit par Smalltalk pour créer des applications de bureau. Un environnement Web se comporte complètement différemment. Cela n'a pas beaucoup de sens de copier un concept vieux de 40 ans à partir du développement de bureau et de le coller dans un environnement Web. Cependant, beaucoup de gens pensent que c'est correct, car leur application compile et renvoie les valeurs correctes. À mon avis, cela ne suffit pas pour déclarer qu'un certain choix de conception est correct.

Un exemple de modèle dans une application Web pourrait être:

public class LoginModel
{
    private readonly AuthenticationService authentication;

    public LoginModel(AuthenticationService authentication)
    {
        this.authentication = authentication;
    }

    public bool Login()
    {
        return authentication.Login(Username, Password);
    }

    public string Username { get; set; }
    public string Password { get; set; }
}

Le contrôleur peut l'utiliser comme ceci:

public class LoginController
{
    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        bool success = model.Login();

        if (success)
        {
            return new RedirectResult("/dashboard");
        }
        else
        {
            TempData["message"] = "Invalid username and/or password";
            return new RedirectResult("/login");
        }
    }
}

Vos méthodes de contrôleur et vos modèles seront petits, facilement testables et précis.

Jeroen
la source
Merci pour la compréhension de l'architecture MVVM, mais pourquoi MVC ne va pas? Votre raisonnement est discutable et suspect de favoritisme. Certes, je ne sais rien de MVVM, mais si une architecture telle que MVC peut imiter le comportement sans avoir à écrire 50 000 lignes de code, alors quel est le problème?
Chef_Code
@Chef_Code: Ce n'est pas discutable ou favoritiste: il suffit de lire l'article original sur MVC. Revenir à la source est bien mieux que de suivre aveuglément le troupeau sans poser de question (aka "meilleures pratiques"). MVC est destiné à des unités beaucoup plus petites: par exemple, un bouton sur un écran est composé d'un modèle, d'une vue et d'un contrôleur. Dans Web-MVC, la page entière a un contrôleur, un modèle et une vue. Le modèle et la vue sont censés être connectés, de sorte que les modifications du modèle se reflètent immédiatement dans la vue et vice versa. Imiter est une très grosse affaire. Une architecture ne doit pas mentir à ses développeurs.
Jeroen
1
@jeroen L'acronyme MVC a été volé et mutilé. Oui, MVC n'a pas de machine virtuelle, mais il n'a pas non plus de référentiel ou de couche de service et ces objets sont largement utilisés dans les sites Web. Je pense que l'OP demande "comment introduire et utiliser une machine virtuelle dans MVC". Dans le nouveau sens de MVC, un modèle n'est pas à la place de la logique métier. La logique métier appartient à une couche de service pour un site Web ou une application de bureau utilisant MVC ou MVVM. Le terme modèle décrit les objets métier qui sont transmis vers / depuis la couche de service. Ces définitions sont très différentes de la description originale de MVC.
Sam
1
@Sam Tout ce qui fait partie d'un site Web ne peut pas être appelé partie de MVC. Il n'y a pas de nouvelle signification de MVC. Il y a le sens correct et le "quelque chose de complètement indépendant que les gens confondent avec le MVC". Dire que le modèle est responsable de la logique métier n'est pas la même chose que la logique métier est codée dans le modèle. La plupart du temps, le modèle sert de façade à l'application.
Jeroen
Le principal défaut que je vois dans le MVC de Microsoft est le verrouillage d'un modèle avec une vue. Cela va à l'encontre du but de toute cette séparation qui a eu lieu dans les conceptions N-Tier au cours des 20 dernières années. Ils ont perdu notre temps à nous forcer à utiliser "WebForms" en 2002 qui était un autre modèle inspiré du bureau hissé sur le Web. Maintenant, ils ont jeté cela, mais ont encore une fois hissé un autre modèle de bureau sur ce nouveau paradigme pour les développeurs Web. Pendant ce temps, Google et d'autres construisent des modèles côté client géants qui séparent tout cela. Je pense que l'ancien ASP VBScript de 1998 était leur véritable système de développement Web.
Stokely
11

Le modèle de vue a est une classe simple qui peut contenir plusieurs propriétés de classe. Nous l'utilisons pour hériter de toutes les propriétés requises, par exemple j'ai deux classes Student et Subject

Public class Student
{
public int Id {get; set;}
public string Name {get; set;}
}  
Public class Subject
{
public int SubjectID {get; set;}
public string SubjectName {get; set;}
}

Maintenant, nous voulons afficher les enregistrements du nom de l'élève et du nom du sujet dans la vue (dans MVC), mais il n'est pas possible d'ajouter plus d'une classe comme:

 @model ProjectName.Model.Student  
 @model ProjectName.Model.Subject

le code ci-dessus générera une erreur ...

Nous créons maintenant une classe et pouvons lui donner n'importe quel nom, mais ce format "XyzViewModel" le rendra plus facile à comprendre. C'est un concept d'héritage. Maintenant, nous créons une troisième classe avec le nom suivant:

public class StudentViewModel:Subject
{
public int ID {get; set;}
public string Name {get; set;}
}

Maintenant, nous utilisons ce ViewModel dans View

@model ProjectName.Model.StudentViewModel

Maintenant, nous pouvons accéder à toutes les propriétés de StudentViewModel et de la classe héritée dans View.

Mayank
la source
10

Beaucoup de grands exemples, laissez-moi vous expliquer de manière claire et croustillante.

ViewModel = Modèle créé pour servir la vue.

La vue ASP.NET MVC ne peut pas avoir plus d'un modèle, donc si nous devons afficher les propriétés de plusieurs modèles dans la vue, ce n'est pas possible. ViewModel sert cet objectif.

Le modèle de vue est une classe de modèle qui ne peut contenir que les propriétés requises pour une vue. Il peut également contenir des propriétés de plusieurs entités (tables) de la base de données. Comme son nom l'indique, ce modèle est créé spécifiquement pour les exigences View.

Quelques exemples de modèles de vue sont ci-dessous

  • Pour répertorier les données de plusieurs entités dans une page de vue, nous pouvons créer un modèle de vue et avoir les propriétés de toutes les entités pour lesquelles nous voulons répertorier les données. Rejoignez ces entités de base de données et définissez les propriétés du modèle de vue et revenez à la vue pour afficher les données des différentes entités sous une forme tabulaire
  • Le modèle de vue peut définir uniquement des champs spécifiques d'une seule entité qui sont requis pour la vue.

ViewModel peut également être utilisé pour insérer, mettre à jour des enregistrements dans plusieurs entités, mais l'utilisation principale de ViewModel est d'afficher les colonnes de plusieurs entités (modèle) dans une seule vue.

La façon de créer ViewModel est identique à la création de modèle, la façon de créer la vue pour le Viewmodel est la même que la création de vue pour le modèle.

Voici un petit exemple de données de liste utilisant ViewModel .

J'espère que cela vous sera utile.

Sheo Narayan
la source
6

ViewModel est une solution de contournement qui corrige la maladresse conceptuelle du framework MVC. Il représente la 4ème couche dans l'architecture Model-View-Controller à 3 couches. lorsque le modèle (modèle de domaine) n'est pas approprié, trop grand (plus de 2 à 3 champs) pour la vue, nous créons un plus petit ViewModel pour le transmettre à la vue.

gsivanov
la source
1

Un modèle de vue est un modèle conceptuel de données. Son utilisation consiste par exemple à obtenir un sous-ensemble ou à combiner des données de différentes tables.

Vous ne souhaiterez peut-être que des propriétés spécifiques, ce qui vous permet de ne charger que celles-ci et non d'autres propriétés inutiles


la source
1
  • ViewModel contient des champs qui sont représentés dans la vue (pour les assistants LabelFor, EditorFor, DisplayFor)
  • ViewModel peut avoir des règles de validation spécifiques à l'aide d'annotations de données ou IDataErrorInfo.
  • ViewModel peut avoir plusieurs entités ou objets de différents modèles de données ou sources de données.

Conception de ViewModel

public class UserLoginViewModel 
{ 
[Required(ErrorMessage = "Please enter your username")] 
[Display(Name = "User Name")]
[MaxLength(50)]
public string UserName { get; set; }
 [Required(ErrorMessage = "Please enter your password")]
 [Display(Name = "Password")]
 [MaxLength(50)]
 public string Password { get; set; } 
} 

Présentation du modèle de vue dans la vue

@model MyModels.UserLoginViewModel 
@{
 ViewBag.Title = "User Login";
 Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm())
{
<div class="editor-label">
 @Html.LabelFor(m => m.UserName)
</div>
<div class="editor-field">
 @Html.TextBoxFor(m => m.UserName)
 @Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="editor-label">
 @Html.LabelFor(m => m.Password)
</div>
<div class="editor-field">
 @Html.PasswordFor(m => m.Password)
 @Html.ValidationMessageFor(m => m.Password)
</div>
<p>
 <input type="submit" value="Log In" />
</p>
</div>
}

Travailler avec l'action

public ActionResult Login()
{ 
return View();
}
[HttpPost]
public ActionResult Login(UserLoginViewModel user)
{
// To acces data using LINQ
DataClassesDataContext mobjentity = new DataClassesDataContext();
 if (ModelState.IsValid) 
{ 
try
 {
 var q = mobjentity.tblUsers.Where(m => m.UserName == user.UserName && m.Password == user.Password).ToList(); 
 if (q.Count > 0) 
 { 
 return RedirectToAction("MyAccount");
 }
 else
 {
 ModelState.AddModelError("", "The user name or password provided is incorrect.");
 }
 }
 catch (Exception ex)
 {
 } 
 } 
 return View(user);
} 
  1. Dans ViewModel, mettez uniquement les champs / données que vous souhaitez afficher sur la vue / page.
  2. Puisque la vue représente à nouveau les propriétés du ViewModel, elle est donc facile pour le rendu et la maintenance.
  3. Utilisez un mappeur lorsque ViewModel devient plus complexe.
codeur sauvage
la source
1

View Model est une classe que nous pouvons utiliser pour le rendu des données sur View. Supposons que vous ayez deux entités Place et PlaceCategory et que vous souhaitiez accéder aux données des deux entités à l'aide d'un seul modèle, nous utilisons ViewModel.

  public class Place
    {
       public int PlaceId { get; set; }
        public string PlaceName { get; set; }
        public string Latitude { get; set; }
        public string Longitude { get; set; }
        public string BestTime { get; set; }
    }
    public class Category
    {
        public int ID { get; set; }
        public int? PlaceId { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }
    public class PlaceCategoryviewModel
    {
        public string PlaceName { get; set; }
        public string BestTime { get; set; }
        public string PlaceCategoryName { get; set; }
        public string PlaceCategoryType { get; set; }
    }

Ainsi, dans l'exemple ci-dessus, Place et Catégorie sont les deux entités différentes et PlaceCategory viewmodel est ViewModel que nous pouvons utiliser sur View.

Sagar Shinde
la source
Vos exemples ne sont pas si clairs. Ce qui est indiqué ci-dessus est qu'un ViewModel connecte des données à sa vue. Si vous regardez les ViewModels dans BlipAjax, vous voyez des classes qui lui conviennent parfaitement.
Herman Van Der Blom
0

Si vous souhaitez étudier le code pour configurer une application Web "Baseline" avec ViewModels, je peux vous conseiller de télécharger ce code sur GitHub: https://github.com/ajsaulsberry/BlipAjax . J'ai développé des applications pour grandes entreprises. Lorsque vous faites cela, il est problématique de configurer une bonne architecture qui gère toutes ces fonctionnalités "ViewModel". Je pense qu'avec BlipAjax, vous aurez une très bonne "ligne de base" pour commencer. C'est juste un site Web simple, mais grand dans sa simplicité. J'aime la façon dont ils ont utilisé la langue anglaise pour montrer ce qui est vraiment nécessaire dans l'application.

Herman Van Der Blom
la source