Modèle de réussite de l'Édition sans un tas de champs cachés
En Bref: Comment puis-je modifier avec succès un DB d'entrée sans avoir besoin d'inclure tous les domaines pour le Modèle à l'intérieur de la Vue Edit?
Mise à JOUR
J'ai donc un élément dans la base de données (un Article). Je veux modifier un article. L'article que j'ai modifier a de nombreuses propriétés (Id, CreatedBy, DateCreated, Title, Body). Certaines de ces propriétés n'a jamais besoin de changer (comme Id, CreatedBy, DateCreated). Donc, dans mon Édition de la Vue, et je ne veux champs de saisie pour les champs qui peuvent être modifiés (comme le Titre, le Corps). Quand j'mettre en œuvre une modification en Vue comme cela, le Modèle de la Liaison échoue. Tous les champs que je n'ai pas d'approvisionnement en intrants pour les obtient une valeur "par défaut" (comme DateCreated obtient mis à 01/01/0001 12:00:00). Si je ne approvisionnement en intrants pour chaque champ, tout fonctionne bien, et l'article est modifié comme prévu. Je ne sais pas si c'est correct de dire que "le Modèle de la Liaison échoue" forcément, si bien que "le système remplit les champs avec des données incorrectes si aucun champ de Saisie a été fourni pour eux dans la Vue d'Édition."
Comment puis-je créer une Vue d'Édition d'une façon telle que j'ai seulement besoin de fournir des champs de saisie pour les champs qui peuvent/doivent montage, de sorte que lorsque la modification de la méthode du Contrôleur est appelé, des domaines comme la DateCreated sont correctement renseignées, et pas définie par défaut, valeur incorrecte? Voici ma méthode Edit tel qu'il est actuellement:
[HttpPost]
public ActionResult Edit(Article article)
{
//Get a list of categories for dropdownlist
ViewBag.Categories = GetDropDownList();
if (article.CreatedBy == (string)CurrentSession.SamAccountName || (bool)CurrentSession.IsAdmin)
{
if (ModelState.IsValid)
{
article.LastUpdatedBy = MyHelpers.SessionBag.Current.SamAccountName;
article.LastUpdated = DateTime.Now;
article.Body = Sanitizer.GetSafeHtmlFragment(article.Body);
_db.Entry(article).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return View(article);
}
//User not allowed to edit
return RedirectToAction("Index", "Home");
}
Et modification de la Vue si cela aide:
. . .
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Article</legend>
<p>
<input type="submit" value="Save" /> | @Html.ActionLink("Back to List", "Index")
</p>
@Html.Action("Details", "Article", new { id = Model.Id })
@Html.HiddenFor(model => model.CreatedBy)
@Html.HiddenFor(model => model.DateCreated)
<div class="editor-field">
<span>
@Html.LabelFor(model => model.Type)
@Html.DropDownListFor(model => model.Type, (SelectList)ViewBag.Categories)
@Html.ValidationMessageFor(model => model.Type)
</span>
<span>
@Html.LabelFor(model => model.Active)
@Html.CheckBoxFor(model => model.Active)
@Html.ValidationMessageFor(model => model.Active)
</span>
<span>
@Html.LabelFor(model => model.Stickied)
@Html.CheckBoxFor(model => model.Stickied)
@Html.ValidationMessageFor(model => model.Stickied)
</span>
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Body)
</div>
<div class="editor-field">
@* We set the id of the TextArea to 'CKeditor' for the CKeditor script to change the TextArea into a WYSIWYG editor. *@
@Html.TextAreaFor(model => model.Body, new { id = "CKeditor", @class = "text-editor" })
@Html.ValidationMessageFor(model => model.Body)
</div>
</fieldset>
. . .
Si je devais quitter ces deux entrées:
@Html.HiddenFor(model => model.CreatedBy)
@Html.HiddenFor(model => model.DateCreated)
lorsque la modification de la méthode est appelée, ils sont définis à leurs valeurs par défaut. CreatedBy est fixé à Null, Créée est fixé à 01/01/0001 12:00:00
Pourquoi ne sont-ils pas les valeurs qu'ils sont actuellement définis dans la DB?
Mais si c'est la "bonne" voie, bien sûr, je veux le savoir maintenant 😉 j'ai été faire un peu de creuser et il semble que, oui, à l'aide de ces choses que l'on appelle "Viewmodel" est le chemin à parcourir: une manière de "montrer une tranche de l'information à partir d'une seule entité". Apparemment, c'est un peu plus de travail, mais aussi de modèles de points de vue deviennent plus complexes qu'ils détiennent fort, alors que ces autres "force brute" méthodes ont tendance à se décomposer.
OriginalL'auteur Caleb Bergman | 2012-06-07
Vous devez vous connecter pour publier un commentaire.
Après un peu plus de recherche, je suis tombé sur quelques outils qui aident dans le ViewModel processus - l'une étant AutoMapper & les autres InjectValues. Je suis allé avec InjectValues principalement parce qu'il peut non seulement "aplatir" les objets (objet map -> b) mais il peut aussi "unflatten" (objet map b -> a) - quelque chose que AutoMapper manque malheureusement d'out-of-the-box - quelque chose que je dois faire pour mettre à jour les valeurs à l'intérieur d'une DB.
Maintenant, au lieu d'envoyer mon Article modèle avec toutes ses propriétés à mon point de vue, j'ai créé un ArticleViewModel contenant uniquement les propriétés suivantes:
Quand je crée un Article, au lieu de l'envoi d'un Article de l'objet (avec chaque propriété) j'envoie le point de Vue d'un "simple" modèle - mon ArticleViewModel:
Pour la méthode POST, nous prenons le ViewModel nous avons envoyé à la Vue et à l'utilisation de ses données pour Créer un nouvel Article dans la base de données. Pour ce faire, nous "unflattening" le ViewModel sur un Article de l'objet:
Les "pièces manquantes" rempli sont les propriétés de l'Article, je ne voulais pas définie dans la Vue, ils ne doivent être mis à jour dans la vue d'Édition (ou pas du tout, d'ailleurs).
La méthode Edit est à peu près le même, sauf qu'au lieu d'envoyer une nouvelle ViewModel de la Vue, nous envoyer un ViewModel pré-rempli avec les données de notre base de données. Nous faisons cela en récupérant l'Article de la base de données et l'aplatissement de données sur le ViewModel. Tout d'abord, la méthode GET:
Pour la méthode POST, nous voulons prendre les données envoyées à partir de la Vue et de la mise à jour de l'Article stockées dans la base de données. Pour ce faire, nous avons simplement inverser le processus d'aplatissement par "unflattening" le ViewModel sur l'Article objet - comme nous l'avons fait pour la version de notre méthode de création:
Nous avons aussi besoin de changer fortement typées Créer & Modifier les points de vue de s'attendre à une ArticleViewModel au lieu d'un Article:
Et c'est tout!
Donc en résumé, vous pouvez mettre en œuvre des ViewModels passer juste pièces de vos Modèles, vos points de Vue. Vous pouvez ensuite mettre à jour juste ces morceaux, passer le ViewModel vers le Contrôleur, et d'utiliser les informations mises à jour dans le ViewModel pour mettre à jour le réelle Modèle.
@Html.HiddenFor(model => model.Id)
+1 pour trouver la Valeur de l'Injecteur à partir de Codeplex. C'est une jolie petite bibliothèque.
OriginalL'auteur Caleb Bergman
Modèle d'affichage exemple:
Exemple de liaison de
Qui est simple exemple, mais vous devriez regarder ModelState de vérifier si le modèle ne comporte pas d'erreurs, vérifier les autorisations et déplacer ce code de contrôleur pour les classes de service, mais
c'est une autre leçon.
C'est corrigé méthode Edit:
C'est de cette façon MVC est destiné à être joué. Les champs cachés peuvent être facilement manipulées et modifiées. Terrain, caché n'est pas de protéger les valeurs de changé. Quelqu'un peut encore changer champ caché et modifier quelque chose, que vous ne voulez pas modifier. Veuillez lire ce récent question: stackoverflow.com/questions/10928287/...
Je pensais que le Modèle de Classeur serait en mesure de trouver un certain Article par son ID passé, remplir chacun des champs lui-même (qui contiennent déjà des valeurs), et seulement de modifier chacun des champs qui ont été modifiés à l'intérieur de la Vue. Mais apparemment (comme cela a été le cas avec moi, de toute façon) si il ne le trouve pas en entrée de terrain passé par la, il va remplir ce champ avec une valeur "par défaut" (par exemple, Un champ DateTime avec "01/01/0001 12:00:00'). Si ce champ DateTime était, disons, le champ DateCreated, évidemment, ce serait faux.
Bon point sur les utilisateurs malveillants d'édition des champs cachés. Donc Viewmodel sont la voie à suivre, hein? Pourriez-vous éventuellement me diriger dans la direction de quelques bons articles (peut-être un livre) qui discute et explique leurs utilisations? Je suis passé par un bon nombre de différents tutoriels et ont été la lecture de quelques livres (l'un étant le "Professionnel ASP.NET MVC 3") et n'ont jamais couru en avant :/
Modèle de Classeur de ne pas récupérer votre article de la base de données et il n'est pas conçu pour faire. Je suis désolé, je n'ai pas compris votre problème. Vous devez récupérer l'article à partir de la base de données vous-même, de mettre à jour ses propriétés avec des valeurs de vue du modèle et l'enregistrer dans la base de données.
OriginalL'auteur LukLed
une autre bonne façon sans viewmodel
OriginalL'auteur Amin
Utilisation Viewmodel.
Grâce à ma poursuite de la recherche de trouver une solution à ce problème, je crois que l'utilisation de ces choses que l'on appelle "Viewmodel" est le chemin à parcourir. Comme expliqué dans un post par Jimmy Bogard, Viewmodel sont un moyen de "montrer une tranche de l'information à partir d'une seule entité."
asp.net mvc-vue-modèle-modèles m'a dirigé sur la bonne voie; je suis toujours en train de vérifier certaines des ressources externes, l'auteur affichés afin de mieux saisir le ViewModel concept (Le blog de Jimmy, est l'un d'eux).
OriginalL'auteur Caleb Bergman
En plus de la réponse, AutoMapper peut également être utilisé pour unflatten.
À l'aide de AutoMapper à unflatten un DTO
OriginalL'auteur stenlytw