Examen de la façon dont ASP.NET MVC génère un modèle automatique du helper DropDownList
par Rick Anderson
Dans Explorateur de solutions, cliquez avec le bouton droit sur le dossier Contrôleurs, puis sélectionnez Ajouter un contrôleur. Nommez le contrôleur StoreManagerController. Définissez les options de la boîte de dialogue Ajouter un contrôleur , comme indiqué dans l’image ci-dessous.
Modifiez la vue StoreManager\Index.cshtml et supprimez AlbumArtUrl
. AlbumArtUrl
La suppression rend la présentation plus lisible. Le code terminé est indiqué ci-dessous.
@model IEnumerable<MvcMusicStore.Models.Album>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
Genre
</th>
<th>
Artist
</th>
<th>
Title
</th>
<th>
Price
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Genre.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Artist.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
@Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
@Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
</td>
</tr>
}
</table>
Ouvrez le fichier Controllers\StoreManagerController.cs et recherchez la Index
méthode. Ajoutez la OrderBy
clause afin que les albums soient triés par prix. Le code complet est illustré ci-dessous.
public ViewResult Index()
{
var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist)
.OrderBy(a => a.Price);
return View(albums.ToList());
}
Le tri par prix facilite le test des modifications apportées à la base de données. Lorsque vous testez les méthodes de modification et de création, vous pouvez utiliser un prix bas afin que les données enregistrées apparaissent en premier.
Ouvrez le fichier StoreManager\Edit.cshtml . Ajoutez la ligne suivante juste après la balise de légende.
@Html.HiddenFor(model => model.AlbumId)
Le code suivant montre le contexte de cette modification :
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Album</legend>
@Html.HiddenFor(model => model.AlbumId)
<div class="editor-label">
@Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("GenreId", String.Empty)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
<!-- Items removed for brevity. -->
}
Il AlbumId
est nécessaire d’apporter des modifications à un enregistrement d’album.
Appuyez sur Ctrl+F5 pour exécuter l’application. Sélectionnez le lien Administrateur , puis sélectionnez le lien Créer un nouvel album. Vérifiez que les informations de l’album ont été enregistrées. Modifiez un album et vérifiez les modifications que vous avez apportées sont conservées.
Schéma de l’album
Le StoreManager
contrôleur créé par le mécanisme de génération automatique MVC permet à CRUD (Créer, Lire, Mettre à jour, Supprimer) l’accès aux albums de la base de données du magasin de musique. Le schéma des informations sur l’album est illustré ci-dessous :
La Albums
table ne stocke pas le genre d’album et la description, elle stocke une clé étrangère dans la Genres
table. Le Genres
tableau contient le nom et la description du genre. De même, la Albums
table ne contient pas le nom des artistes de l’album, mais une clé étrangère à la Artists
table. La Artists
table contient le nom de l’artiste. Si vous examinez les données de la Albums
table, vous pouvez voir chaque ligne contient une clé étrangère dans la Genres
table et une clé étrangère dans la Artists
table. L’image ci-dessous montre certaines données de tableau de la Albums
table.
Balise De sélection HTML
L’élément HTML <select>
(créé par l’assistance HTML DropDownList ) est utilisé pour afficher une liste complète de valeurs (telles que la liste des genres). Pour les formulaires de modification, lorsque la valeur actuelle est connue, la liste de sélection peut afficher la valeur actuelle. Nous l’avons vu précédemment lorsque nous avons défini la valeur sélectionnée sur Comédie. La liste de sélection est idéale pour afficher les données de catégorie ou de clé étrangère. L’élément <select>
de la clé étrangère Genre affiche la liste des noms de genre possibles, mais lorsque vous enregistrez le formulaire, la propriété Genre est mise à jour avec la valeur de clé étrangère Genre, et non le nom de genre affiché. Dans l’image ci-dessous, le genre sélectionné est Disco et l’artiste est Donna Summer.
Examen du code généré automatique ASP.NET MVC
Ouvrez le fichier Controllers\StoreManagerController.cs et recherchez la HTTP GET Create
méthode.
public ActionResult Create()
{
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
return View();
}
La Create
méthode ajoute deux objets SelectList à l’objet ViewBag
, un pour contenir les informations de genre et un pour contenir les informations de l’artiste. La surcharge du constructeur SelectList utilisée ci-dessus prend trois arguments :
public SelectList(
IEnumerable items,
string dataValueField,
string dataTextField
)
- éléments : IEnumerable contenant les éléments de la liste. Dans l’exemple ci-dessus, la liste des genres retournés par
db.Genres
. - dataValueField : nom de la propriété dans la liste IEnumerable qui contient la valeur de clé. Dans l’exemple ci-dessus,
GenreId
etArtistId
. - dataTextField : nom de la propriété dans la liste IEnumerable qui contient les informations à afficher. Dans les artistes et la table de genre, le
name
champ est utilisé.
Ouvrez le fichier Views\StoreManager\Create.cshtml et examinez le Html.DropDownList
balisage d’assistance du champ genre.
@model MvcMusicStore.Models.Album
@* Markup removed for clarity.*@
@Html.DropDownList("GenreId", String.Empty)
La première ligne montre que la vue de création prend un Album
modèle. Dans la Create
méthode indiquée ci-dessus, aucun modèle n’a été passé, de sorte que la vue obtient un modèle NullAlbum
. À ce stade, nous créons un nouvel album de sorte que nous n’avons Album
pas de données pour elle.
La surcharge Html.DropDownList indiquée ci-dessus prend le nom du champ à lier au modèle. Il utilise également ce nom pour rechercher un objet ViewBag contenant un objet SelectList . À l’aide de cette surcharge, vous devez nommer l’objet GenreId
ViewBag SelectList. Le deuxième paramètre (String.Empty
) est le texte à afficher lorsqu’aucun élément n’est sélectionné. C’est exactement ce que nous voulons lors de la création d’un nouvel album. Si vous avez supprimé le deuxième paramètre et utilisé le code suivant :
@Html.DropDownList("GenreId")
La liste de sélection est par défaut le premier élément ou Rock dans notre exemple.
Examen de la HTTP POST Create
méthode.
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album)
{
if (ModelState.IsValid)
{
db.Albums.Add(album);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name",
album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name",
album.ArtistId);
return View(album);
}
Cette surcharge de la Create
méthode prend un album
objet, créé par le système de liaison de modèle MVC ASP.NET à partir des valeurs de formulaire publiées. Lorsque vous envoyez un nouvel album, si l’état du modèle est valide et qu’il n’y a pas d’erreur de base de données, le nouvel album est ajouté à la base de données. L’image suivante montre la création d’un nouvel album.
Vous pouvez utiliser l’outil fiddler pour examiner les valeurs de formulaire publiées qui ASP.NET liaison de modèle MVC l’utilise pour créer l’objet album.
.
Refactorisation de la création de ViewBag SelectList
Edit
Les méthodes et la HTTP POST Create
méthode ont un code identique pour configurer selectList dans ViewBag. Dans l’esprit de DRY, nous refactoriserons ce code. Nous allons utiliser ce code refactorisé ultérieurement.
Créez une méthode pour ajouter un genre et un artiste SelectList à ViewBag.
private void SetGenreArtistViewBag(int? GenreID = null, int? ArtistID = null) {
if (GenreID == null)
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
else
ViewBag.GenreId = new SelectList(db.Genres.ToArray(), "GenreId", "Name", GenreID);
if (ArtistID == null)
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
else
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", ArtistID);
}
Remplacez les deux lignes définissant les ViewBag
deux lignes dans chacune des Create
méthodes et Edit
par un appel à la SetGenreArtistViewBag
méthode. Le code terminé est indiqué ci-dessous.
//
// GET: /StoreManager/Create
public ActionResult Create() {
SetGenreArtistViewBag();
return View();
}
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album) {
if (ModelState.IsValid) {
db.Albums.Add(album);
db.SaveChanges();
return RedirectToAction("Index");
}
SetGenreArtistViewBag(album.GenreId, album.ArtistId);
return View(album);
}
//
// GET: /StoreManager/Edit/5
public ActionResult Edit(int id) {
Album album = db.Albums.Find(id);
SetGenreArtistViewBag(album.GenreId, album.ArtistId);
return View(album);
}
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(Album album) {
if (ModelState.IsValid) {
db.Entry(album).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
SetGenreArtistViewBag(album.GenreId, album.ArtistId);
return View(album);
}
Créez un nouvel album et modifiez un album pour vérifier le travail des modifications.
Passage explicite de la selectList à la liste déroulante
Les vues de création et de modification créées par la structure ASP.NET MVC utilisent la surcharge DropDownList suivante :
public static MvcHtmlString DropDownList(
this HtmlHelper htmlHelper,
string name, // The name of the ViewModel property to bind.
string optionLabel // The string added to the top of the list
// typically String.Empty or "Select a Genre"
)
Le DropDownList
balisage de la vue de création est illustré ci-dessous.
@Html.DropDownList("GenreId", String.Empty)
Étant donné que la ViewBag
propriété de l’objet SelectList
est nommée GenreId
, l’assistance DropDownList utilise laGenreId
SelectList dans ViewBag. Dans la surcharge DropDownList suivante, celle-ci SelectList
est transmise explicitement.
public static MvcHtmlString DropDownList(
this HtmlHelper htmlHelper,
string name, // The name of the ViewModel property to bind.
IEnumerable selectList // The SelectList
)
Ouvrez le fichier Views\StoreManager\Edit.cshtml et modifiez l’appel DropDownList pour passer explicitement la liste de sélection à l’aide de la surcharge ci-dessus. Procédez comme suit pour la catégorie Genre. Le code terminé est illustré ci-dessous :
@Html.DropDownList("GenreId", ViewBag.GenreId as SelectList)
Exécutez l’application et cliquez sur le lien Administrateur , puis accédez à un album Jazz et sélectionnez le lien Modifier .
Au lieu de montrer jazz comme genre actuellement sélectionné, Rock est affiché. Lorsque l’argument de chaîne (propriété à lier) et l’objet SelectList ont le même nom, la valeur sélectionnée n’est pas utilisée. Lorsqu’aucune valeur sélectionnée n’est fournie, les navigateurs sont par défaut le premier élément de SelectList (qui est Rock dans l’exemple ci-dessus). Il s’agit d’une limitation connue de l’assistance DropDownList .
Ouvrez le fichier Controllers\StoreManagerController.cs et modifiez les noms d’objets SelectList en Genres
et Artists
. Le code terminé est illustré ci-dessous :
private void SetGenreArtistViewBag(int? GenreID = null, int? ArtistID = null) {
if (GenreID == null)
ViewBag.Genres = new SelectList(db.Genres, "GenreId", "Name");
else
ViewBag.Genres = new SelectList(db.Genres.ToArray(), "GenreId", "Name", GenreID);
if (ArtistID == null)
ViewBag.Artists = new SelectList(db.Artists, "ArtistId", "Name");
else
ViewBag.Artists = new SelectList(db.Artists, "ArtistId", "Name", ArtistID);
}
Les noms Genres et Artistes sont de meilleurs noms pour les catégories, car ils contiennent plus que l’ID de chaque catégorie. La refactorisation que nous avons fait précédemment a payé. Au lieu de modifier ViewBag dans quatre méthodes, nos modifications ont été isolées de la SetGenreArtistViewBag
méthode.
Modifiez l’appel DropDownList dans les vues de création et de modification pour utiliser les nouveaux noms SelectList . Le nouveau balisage de la vue d’édition est illustré ci-dessous :
<div class="editor-label">
@Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("GenreId", ViewBag.Genres as SelectList)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ArtistId, "Artist")
</div>
<div class="editor-field">
@Html.DropDownList("ArtistId", ViewBag.Artists as SelectList)
@Html.ValidationMessageFor(model => model.ArtistId)
</div>
La vue Créer nécessite une chaîne vide pour empêcher l’affichage du premier élément de la Liste de sélection.
<div class="editor-label">
@Html.LabelFor(model => model.GenreId, "Genre" )
</div>
<div class="editor-field">
@Html.DropDownList("GenreId", ViewBag.Genres as SelectList, String.Empty)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ArtistId, "Artist")
</div>
<div class="editor-field">
@Html.DropDownList("ArtistId", ViewBag.Artists as SelectList, String.Empty)
@Html.ValidationMessageFor(model => model.ArtistId)
</div>
Créez un nouvel album et modifiez un album pour vérifier le travail des modifications. Testez le code de modification en sélectionnant un album avec un genre autre que Rock.
Utilisation d’un modèle d’affichage avec l’assistance DropDownList
Créez une classe dans le dossier ViewModels nommé AlbumSelectListViewModel
. Remplacez le code dans la AlbumSelectListViewModel
classe par les éléments suivants :
using MvcMusicStore.Models;
using System.Web.Mvc;
using System.Collections;
namespace MvcMusicStore.ViewModels {
public class AlbumSelectListViewModel {
public Album Album { get; private set; }
public SelectList Artists { get; private set; }
public SelectList Genres { get; private set; }
public AlbumSelectListViewModel(Album album,
IEnumerable artists,
IEnumerable genres) {
Album = album;
Artists = new SelectList(artists, "ArtistID", "Name", album.ArtistId);
Genres = new SelectList(genres, "GenreID", "Name", album.GenreId);
}
}
}
Le AlbumSelectListViewModel
constructeur prend un album, une liste d’artistes et de genres et crée un objet contenant l’album et un SelectList
pour les genres et les artistes.
Générez le projet afin que celui-ci AlbumSelectListViewModel
soit disponible lorsque nous créons une vue à l’étape suivante.
Ajoutez une EditVM
méthode au StoreManagerController
. Le code terminé est indiqué ci-dessous.
//
// GET: /StoreManager/EditVM/5
public ActionResult EditVM(int id) {
Album album = db.Albums.Find(id);
if (album == null)
return HttpNotFound();
AlbumSelectListViewModel aslvm = new AlbumSelectListViewModel(album, db.Artists, db.Genres);
return View(aslvm);
}
Cliquez avec le bouton AlbumSelectListViewModel
droit sur Résoudre, puis utilisez MvcMusicStore.ViewModels ;.
Vous pouvez également ajouter l’instruction using suivante :
using MvcMusicStore.ViewModels;
Cliquez avec le bouton EditVM
droit et sélectionnez Ajouter un affichage. Utilisez les options indiquées ci-dessous.
Sélectionnez Ajouter, puis remplacez le contenu du fichier Views\StoreManager\EditVM.cshtml par les éléments suivants :
@model MvcMusicStore.ViewModels.AlbumSelectListViewModel
@{
ViewBag.Title = "EditVM";
}
<h2>Edit VM</h2>
@using (Html.BeginForm("Edit","StoreManager",FormMethod.Post)) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Album</legend>
@Html.HiddenFor(model => model.Album.AlbumId )
<div class="editor-label">
@Html.LabelFor(model => model.Album.GenreId, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("Album.GenreId", Model.Genres)
@Html.ValidationMessageFor(model => model.Album.GenreId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.ArtistId, "Artist")
</div>
<div class="editor-field">
@Html.DropDownList("Album.ArtistId", Model.Artists)
@Html.ValidationMessageFor(model => model.Album.ArtistId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Album.Title)
@Html.ValidationMessageFor(model => model.Album.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.Price)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Album.Price)
@Html.ValidationMessageFor(model => model.Album.Price)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.AlbumArtUrl)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Album.AlbumArtUrl)
@Html.ValidationMessageFor(model => model.Album.AlbumArtUrl)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Le EditVM
balisage est très similaire au balisage d’origine Edit
avec les exceptions suivantes.
- Les propriétés du modèle dans la
Edit
vue sont de la formemodel.property
(par exemple).model.Title
Les propriétés du modèle dans laEditVm
vue sont de la formemodel.Album.property
(par exemple).model.Album.Title
C’est parce que laEditVM
vue est passée à un conteneur pour unAlbum
, pas commeAlbum
dans laEdit
vue. - Le deuxième paramètre DropDownList provient du modèle de vue, et non de ViewBag.
- L’assistance BeginForm dans la
EditVM
vue publie explicitement la méthode d’actionEdit
. En revenant à l’actionEdit
, nous n’avons pas besoin d’écrire uneHTTP POST EditVM
action et pouvons réutiliser l’actionHTTP POST
Edit
.
Exécutez l’application et modifiez un album. Modifiez l’URL à utiliser EditVM
. Modifiez un champ et appuyez sur le bouton Enregistrer pour vérifier que le code fonctionne.
Quelle approche devez-vous utiliser ?
Les trois approches présentées sont acceptables. De nombreux développeurs préfèrent transmettre explicitement l’utilisation SelectList
de l’objet ViewBag
DropDownList
. Cette approche présente l’avantage supplémentaire de vous donner la flexibilité d’utiliser un nom plus approprié pour la collection. La seule mise en garde est que vous ne pouvez pas nommer l’objet ViewBag SelectList
le même nom que la propriété de modèle.
Certains développeurs préfèrent l’approche ViewModel. D’autres considèrent le balisage plus détaillé et le code HTML généré de l’approche ViewModel un inconvénient.
Dans cette section, nous avons appris trois approches de l’utilisation de DropDownList avec des données de catégorie. Dans la section suivante, nous allons montrer comment ajouter une nouvelle catégorie.