Partager via


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.

Image de Explorateur de solutions boîte de dialogue Ajouter un contrôleur

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 :

Image du schéma album

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.

Image de certaines données de la table Albums

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.

Image du genre disco sélectionné

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

)
  1. éléments : IEnumerable contenant les éléments de la liste. Dans l’exemple ci-dessus, la liste des genres retournés par db.Genres.
  2. dataValueField : nom de la propriété dans la liste IEnumerable qui contient la valeur de clé. Dans l’exemple ci-dessus, GenreId et ArtistId.
  3. 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 GenreIdViewBag 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.

Image du premier élément par défaut

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.

Image montrant 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.

Image de l’outil Fiddler.

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 .

Image de la sélection de l’album Jazz à 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 AlbumSelectListViewModeldroit sur Résoudre, puis utilisez MvcMusicStore.ViewModels ;.

Sélection d’images de résolution

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.

Image montrant la boîte de dialogue Ajouter un affichage

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 forme model.property(par exemple). model.Title Les propriétés du modèle dans la EditVm vue sont de la forme model.Album.property(par exemple). model.Album.Title C’est parce que la EditVM vue est passée à un conteneur pour un Album, pas comme Album dans la Edit 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’action Edit . En revenant à l’action Edit , nous n’avons pas besoin d’écrire une HTTP POST EditVM action et pouvons réutiliser l’action HTTP 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.

Image avec U R L modification de V M

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 ViewBagDropDownList . 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.