Examen des méthodes de modification et de la vue de modification (C#)
par Rick Anderson
Remarque
Une version mise à jour de ce didacticiel est disponible ici qui utilise ASP.NET MVC 5 et Visual Studio 2013. Il est plus sécurisé, beaucoup plus simple à suivre et montre plus de fonctionnalités.
Ce tutoriel vous apprend les principes de base de la création d’une application web MVC ASP.NET à l’aide de Microsoft Visual Web Developer 2010 Express Service Pack 1, qui est une version gratuite de Microsoft Visual Studio. Avant de commencer, vérifiez que vous avez installé les conditions préalables répertoriées ci-dessous. Vous pouvez les installer en cliquant sur le lien suivant : Web Platform Installer. Vous pouvez également installer individuellement les prérequis à l’aide des liens suivants :
- Conditions préalables pour Visual Studio Web Developer Express SP1
- mise à jour des outils MVC 3 ASP.NET
- SQL Server Compact 4.0(runtime + prise en charge des outils)
Si vous utilisez Visual Studio 2010 au lieu de Visual Web Developer 2010, installez les prérequis en cliquant sur le lien suivant : Conditions préalables pour Visual Studio 2010.
Un projet Visual Web Developer avec du code source C# est disponible pour accompagner cette rubrique. Téléchargez la version C#. Si vous préférez Visual Basic, basculez vers la version Visual Basic de ce didacticiel.
Dans cette section, vous allez examiner les méthodes et vues d’action générées pour le contrôleur de film. Ensuite, vous allez ajouter une page de recherche personnalisée.
Exécutez l’application et accédez au Movies
contrôleur en ajoutant /Movies à l’URL dans la barre d’adresse de votre navigateur. Maintenez le pointeur de la souris sur un lien Modifier pour afficher l’URL vers laquelle il est lié.
Le lien Edit a été généré par la Html.ActionLink
méthode dans la vue Views\Movies\Index.cshtml :
@Html.ActionLink("Edit", "Edit", new { id=item.ID })
L’objet Html
est un assistance exposé à l’aide d’une propriété sur la WebViewPage
classe de base. La ActionLink
méthode de l’assistance facilite la génération dynamique de liens hypertexte HTML qui relient les méthodes d’action sur les contrôleurs. Le premier argument de la ActionLink
méthode est le texte du lien à afficher (par exemple). <a>Edit Me</a>
Le deuxième argument est le nom de la méthode d’action à appeler. L’argument final est un objet anonyme qui génère les données de routage (dans ce cas, l’ID de 4).
Le lien généré affiché dans l’image précédente est http://localhost:xxxxx/Movies/Edit/4
. L’itinéraire par défaut prend le modèle {controller}/{action}/{id}
d’URL . Par conséquent, ASP.NET se traduit http://localhost:xxxxx/Movies/Edit/4
en une requête en méthode Edit
d’action du Movies
contrôleur avec le paramètre ID
égal à 4.
Vous pouvez également passer des paramètres de méthode d’action à l’aide d’une chaîne de requête. Par exemple, l’URL http://localhost:xxxxx/Movies/Edit?ID=4
transmet également le paramètre ID
4 à la Edit
méthode d’action du Movies
contrôleur.
Ouvrez le Movies
contrôleur. Les deux Edit
méthodes d’action sont indiquées ci-dessous.
//
// GET: /Movies/Edit/5
public ActionResult Edit(int id)
{
Movie movie = db.Movies.Find(id);
return View(movie);
}
//
// POST: /Movies/Edit/5
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
Notez que la deuxième méthode d’action Edit
est précédée de l’attribut HttpPost
. Cet attribut spécifie que la surcharge de la Edit
méthode peut être appelée uniquement pour les requêtes POST. Vous pouvez appliquer l’attribut HttpGet
à la première méthode d’édition, mais cela n’est pas nécessaire, car il s’agit de la valeur par défaut. (Nous allons faire référence à des méthodes d’action qui sont implicitement affectées à l’attribut HttpGet
en tant que HttpGet
méthodes.)
La HttpGet
Edit
méthode prend le paramètre ID de film, recherche le film à l’aide de la méthode Entity Framework Find
et retourne le film sélectionné en mode Édition. Quand le système de génération de modèles automatique a créé la vue Edit, il a examiné la classe Movie
et a créé le code pour restituer les éléments <label>
et <input>
de chaque propriété de la classe. L’exemple suivant montre l’affichage Modifier qui a été généré :
@model MvcMovie.Models.Movie
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
@Html.HiddenFor(model => model.ID)
<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.ReleaseDate)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ReleaseDate)
@Html.ValidationMessageFor(model => model.ReleaseDate)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Genre)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Genre)
@Html.ValidationMessageFor(model => model.Genre)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Price)
@Html.ValidationMessageFor(model => model.Price)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Notez que le modèle d’affichage a une @model MvcMovie.Models.Movie
instruction en haut du fichier . Cela spécifie que la vue s’attend à ce que le modèle de vue soit de type Movie
.
Le code généré automatiquement utilise plusieurs méthodes d’assistance pour simplifier le balisage HTML. L’assistance Html.LabelFor
affiche le nom du champ (« Title », « ReleaseDate », « Genre » ou « Price »). L’assistance Html.EditorFor
affiche un élément HTML <input>
. L’assistance Html.ValidationMessageFor
affiche les messages de validation associés à cette propriété.
Exécutez l’application et accédez à l’URL /Movies . Cliquez sur un lien Edit. Dans le navigateur, affichez la source de la page. Le code HTML de la page ressemble à l’exemple suivant. (Le balisage du menu a été exclu pour plus de clarté.)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Edit</title>
<link href="/Content/Site.css" rel="stylesheet" type="text/css" />
<script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
<script src="/Scripts/modernizr-1.7.min.js" type="text/javascript"></script>
</head>
<body>
<div class="page">
<header>
<div id="title">
<h1>MVC Movie App</h1>
</div>
...
</header>
<section id="main">
<h2>Edit</h2>
<script src="/Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>
<form action="/Movies/Edit/4" method="post"> <fieldset>
<legend>Movie</legend>
<input data-val="true" data-val-number="The field ID must be a number."
data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" />
<div class="editor-label">
<label for="Title">Title</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="Title" name="Title" type="text" value="Rio Bravo" />
<span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="ReleaseDate">ReleaseDate</label>
</div>
<div class="editor-field">
<input class="text-box single-line" data-val="true" data-val-required="The ReleaseDate field is required."
id="ReleaseDate" name="ReleaseDate" type="text" value="4/15/1959 12:00:00 AM" />
<span class="field-validation-valid" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="Genre">Genre</label>
</div>
<div class="editor-field">
<input class="text-box single-line" id="Genre" name="Genre" type="text" value="Western" />
<span class="field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
<div class="editor-label">
<label for="Price">Price</label>
</div>
<div class="editor-field">
<input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number."
data-val-required="The Price field is required." id="Price" name="Price" type="text" value="9.99" />
<span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
</form>
<div>
<a href="/Movies">Back to List</a>
</div>
</section>
<footer>
</footer>
</div>
</body>
</html>
Les <input>
éléments se trouvent dans un élément HTML <form>
dont action
l’attribut est défini pour publier sur l’URL /Movies/Edit . Les données de formulaire sont publiées sur le serveur lorsque le bouton Modifier est cliqué.
Traitement de la requête POST
Le code suivant montre la version HttpPost
de la méthode d’action Edit
.
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
Le classeur de modèles d’infrastructure ASP.NET prend les valeurs de formulaire publiées et crée un Movie
objet passé en tant que movie
paramètre. La ModelState.IsValid
vérification du code vérifie que les données soumises dans le formulaire peuvent être utilisées pour modifier un Movie
objet. Si les données sont valides, le code enregistre les données de film dans la Movies
collection de l’instance MovieDBContext
. Le code enregistre ensuite les nouvelles données vidéo dans la base de données en appelant la SaveChanges
méthode de MovieDBContext
, qui conserve les modifications apportées à la base de données. Après avoir enregistré les données, le code redirige l’utilisateur vers la Index
méthode d’action de la MoviesController
classe, ce qui entraîne l’affichage du film mis à jour dans la liste des films.
Si les valeurs publiées ne sont pas valides, elles sont réaffichées dans le formulaire. Les Html.ValidationMessageFor
helpers du modèle d’affichage Edit.cshtml s’occupent de l’affichage des messages d’erreur appropriés.
Remarque sur les paramètres régionaux Si vous travaillez normalement avec des paramètres régionaux autres que l’anglais, consultez Prise en charge ASP.NET validation MVC 3 avec des paramètres régionaux non anglais.
Rendre la méthode Edit plus robuste
La HttpGet
Edit
méthode générée par le système de génération automatique ne vérifie pas que l’ID passé à celui-ci est valide. Si un utilisateur supprime le segment d’ID de l’URL (http://localhost:xxxxx/Movies/Edit
), l’erreur suivante s’affiche :
Un utilisateur peut également transmettre un ID qui n’existe pas dans la base de données, par http://localhost:xxxxx/Movies/Edit/1234
exemple . Vous pouvez apporter deux modifications à la méthode d’action HttpGet
Edit
pour résoudre cette limitation. Tout d’abord, modifiez le ID
paramètre pour avoir la valeur par défaut zéro lorsqu’un ID n’est pas transmis explicitement. Vous pouvez également vérifier que la Find
méthode a réellement trouvé un film avant de renvoyer l’objet film au modèle d’affichage. La méthode mise à jour Edit
est indiquée ci-dessous.
public ActionResult Edit(int id = 0)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
Si aucun film n’est trouvé, la HttpNotFound
méthode est appelée.
Toutes les HttpGet
méthodes suivent un modèle similaire. Ils obtiennent un objet vidéo (ou liste d’objets, dans le cas de Index
), et passent le modèle à la vue. La Create
méthode transmet un objet vidéo vide à l’affichage Create. Toutes les méthodes qui créent, modifient, suppriment ou changent d’une quelconque manière des données le font dans la surcharge HttpPost
de la méthode. La modification de données dans une méthode HTTP GET est un risque de sécurité. La modification de données dans une méthode GET enfreint également les meilleures pratiques HTTP et le modèle REST architectural, qui spécifie que les requêtes GET ne doivent pas changer l’état de votre application. En d’autres termes, l’exécution d’une opération GET doit être une opération sécurisée qui n’a aucun effet secondaire.
Ajout d’une méthode de recherche et d’une vue de recherche
Dans cette section, vous allez ajouter une méthode d’action SearchIndex
qui vous permet de rechercher des films par genre ou par nom. Cette opération sera disponible à l’aide de l’URL /Movies/SearchIndex . La demande affiche un formulaire HTML qui contient des éléments d’entrée qu’un utilisateur peut remplir pour rechercher un film. Lorsqu’un utilisateur envoie le formulaire, la méthode d’action obtient les valeurs de recherche publiées par l’utilisateur et utilise les valeurs pour rechercher la base de données.
Affichage du formulaire SearchIndex
Commencez par ajouter une méthode d’action SearchIndex
à la classe existante MoviesController
. La méthode retourne une vue qui contient un formulaire HTML. Voici le code :
public ActionResult SearchIndex(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
La première ligne de la SearchIndex
méthode crée la requête LINQ suivante pour sélectionner les films :
var movies = from m in db.Movies
select m;
La requête est définie à ce stade, mais n’a pas encore été exécutée sur le magasin de données.
Si le searchString
paramètre contient une chaîne, la requête films est modifiée pour filtrer la valeur de la chaîne de recherche à l’aide du code suivant :
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
Les requêtes LINQ ne sont pas exécutées lorsqu’elles sont définies ou lorsqu’elles sont modifiées en appelant une méthode telle que Where
ou OrderBy
. Au lieu de cela, l’exécution de la requête est différée, ce qui signifie que l’évaluation d’une expression est retardée jusqu’à ce que sa valeur réalisée soit réellement itérée ou que la ToList
méthode soit appelée. Dans l’exemple SearchIndex
, la requête est exécutée dans la vue SearchIndex. Pour plus d’informations sur l’exécution différée des requêtes, consultez Exécution de requête.
Vous pouvez maintenant implémenter l’affichage SearchIndex
qui affichera le formulaire à l’utilisateur. Cliquez avec le bouton droit dans la SearchIndex
méthode, puis cliquez sur Ajouter un affichage. Dans la boîte de dialogue Ajouter un affichage , spécifiez que vous allez passer un Movie
objet au modèle d’affichage en tant que classe de modèle. Dans la liste des modèles de structure Scaffold , choisissez Liste, puis cliquez sur Ajouter.
Lorsque vous cliquez sur le bouton Ajouter , le modèle de vue Views\Movies\SearchIndex.cshtml est créé. Étant donné que vous avez sélectionné Liste dans la liste des modèles de structure , Visual Web Developer a généré automatiquement (généré automatiquement) du contenu par défaut dans l’affichage. La structure a créé un formulaire HTML. Il a examiné la classe et créé du Movie
code pour afficher <label>
les éléments pour chaque propriété de la classe. La liste ci-dessous montre la vue Créer qui a été générée :
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "SearchIndex";
}
<h2>SearchIndex</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
Title
</th>
<th>
ReleaseDate
</th>
<th>
Genre
</th>
<th>
Price
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
@Html.ActionLink("Details", "Details", new { id=item.ID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
Exécutez l’application et accédez à /Movies/SearchIndex. Ajoutez une chaîne de requête comme ?searchString=ghost
à l’URL. Les films filtrés sont affichés.
Si vous modifiez la signature de la SearchIndex
méthode pour avoir un paramètre nommé id
, le id
paramètre correspond à l’espace {id}
réservé pour les itinéraires par défaut définis dans le fichier Global.asax .
{controller}/{action}/{id}
La méthode modifiée SearchIndex
se présente comme suit :
public ActionResult SearchIndex(string id)
{
string searchString = id;
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Vous pouvez maintenant passer le titre de la recherche en tant que données de routage (un segment de l’URL) et non pas en tant que valeur de chaîne de requête.
Cependant, vous ne pouvez pas attendre des utilisateurs qu’ils modifient l’URL à chaque fois qu’ils veulent rechercher un film. Vous allez donc ajouter l’interface utilisateur pour les aider à filtrer les films. Si vous avez modifié la signature de la SearchIndex
méthode pour tester comment passer le paramètre ID lié à l’itinéraire, remplacez-le afin que votre SearchIndex
méthode accepte un paramètre de chaîne nommé searchString
:
public ActionResult SearchIndex(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Ouvrez le fichier Views\Movies\SearchIndex.cshtml , puis juste après @Html.ActionLink("Create New", "Create")
, ajoutez ce qui suit :
@using (Html.BeginForm()){
<p> Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" /></p>
}
L’exemple suivant montre une partie du fichier Views\Movies\SearchIndex.cshtml avec le balisage de filtrage ajouté.
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "SearchIndex";
}
<h2>SearchIndex</h2>
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm()){
<p> Title: @Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
</p>
L’assistance Html.BeginForm
crée une balise d’ouverture <form>
. L’assistance Html.BeginForm
entraîne la publication du formulaire lorsque l’utilisateur envoie le formulaire en cliquant sur le bouton Filtrer .
Exécutez l’application et essayez de rechercher un film.
Il n’y a pas HttpPost
de surcharge de la SearchIndex
méthode. Vous n’en avez pas besoin, car la méthode ne change pas l’état de l’application, en filtrant simplement les données.
Vous pourriez ajouter la méthode HttpPost SearchIndex
suivante. Dans ce cas, l’appelant d’action correspondrait à la HttpPost SearchIndex
méthode et la HttpPost SearchIndex
méthode s’exécuterait comme indiqué dans l’image ci-dessous.
[HttpPost]
public string SearchIndex(FormCollection fc, string searchString)
{
return "<h3> From [HttpPost]SearchIndex: " + searchString + "</h3>";
}
Cependant, même si vous ajoutez cette version HttpPost
de la méthode SearchIndex
, il existe une limitation dans la façon dont tout ceci a été implémenté. Imaginez que vous voulez insérer un signet pour une recherche spécifique, ou que vous voulez envoyer un lien à vos amis sur lequel ils peuvent cliquer pour afficher la même liste filtrée de films. Notez que l’URL de la requête HTTP POST est identique à l’URL de la requête GET (localhost :xxxxx/Movies/SearchIndex) : il n’existe aucune information de recherche dans l’URL elle-même. À l’heure actuelle, les informations de chaîne de recherche sont envoyées au serveur en tant que valeur de champ de formulaire. Cela signifie que vous ne pouvez pas capturer ces informations de recherche à signet ou à envoyer à des amis dans une URL.
La solution consiste à utiliser une surcharge de BeginForm
ce qui spécifie que la requête POST doit ajouter les informations de recherche à l’URL et qui doit être routée vers la version HttpGet de la SearchIndex
méthode. Remplacez la méthode sans BeginForm
paramètre existante par les éléments suivants :
@using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get))
Maintenant, lorsque vous envoyez une recherche, l’URL contient une chaîne de requête de recherche. La recherche accède également à la méthode d’action HttpGet SearchIndex
, même si vous avez une méthode HttpPost SearchIndex
.
Ajout de la recherche par genre
Si vous avez ajouté la HttpPost
version de la méthode, supprimez-la SearchIndex
maintenant.
Ensuite, vous allez ajouter une fonctionnalité pour permettre aux utilisateurs de rechercher des films par genre. Remplacez la méthode SearchIndex
par le code suivant :
public ActionResult SearchIndex(string movieGenre, string searchString)
{
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (string.IsNullOrEmpty(movieGenre))
return View(movies);
else
{
return View(movies.Where(x => x.Genre == movieGenre));
}
}
Cette version de la SearchIndex
méthode prend un paramètre supplémentaire, à savoir movieGenre
. Les premières lignes de code créent un List
objet pour contenir des genres vidéo à partir de la base de données.
Le code suivant est une requête LINQ qui récupère tous les genres dans la base de données.
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
Le code utilise la AddRange
méthode de la collection générique List
pour ajouter tous les genres distincts à la liste. (Sans le modificateur, les Distinct
genres en double seraient ajoutés , par exemple, la comédie serait ajoutée deux fois dans notre exemple). Le code stocke ensuite la liste des genres dans l’objet ViewBag
.
Le code suivant montre comment vérifier le movieGenre
paramètre. S’il n’est pas vide, le code limite davantage la requête films pour limiter les films sélectionnés au genre spécifié.
if (string.IsNullOrEmpty(movieGenre))
return View(movies);
else
{
return View(movies.Where(x => x.Genre == movieGenre));
}
Ajout de balisage à la vue SearchIndex pour prendre en charge la recherche par genre
Ajoutez un Html.DropDownList
assistance au fichier Views\Movies\SearchIndex.cshtml , juste avant l’assistance TextBox
. Le balisage terminé est illustré ci-dessous :
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm()){
<p>Genre: @Html.DropDownList("movieGenre", "All")
Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" /></p>
}
</p>
Exécutez l’application et accédez à /Movies/SearchIndex. Essayez une recherche par genre, par nom de film, et par les deux critères.
Dans cette section, vous avez examiné les méthodes d’action CRUD et les vues générées par l’infrastructure. Vous avez créé une méthode d’action de recherche et une vue qui permettent aux utilisateurs de rechercher par titre et genre de film. Dans la section suivante, vous allez découvrir comment ajouter une propriété au Movie
modèle et comment ajouter un initialiseur qui créera automatiquement une base de données de test.