Partie 5, Mettre à jour les pages générées dans une application ASP.NET Core
Remarque
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 8 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la Stratégie de prise en charge de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 8 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 8 de cet article.
L’application de gestion des films générée est un bon début, mais la présentation n’est pas idéale. ReleaseDate doit être écrit en deux mots, Release Date.
Mettre le modèle à niveau
Mettez à jour Models/Movie.cs
avec le code mis en évidence suivant :
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models;
public class Movie
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; } = string.Empty;
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
Dans le code précédent :
- L’annotation de données
[Column(TypeName = "decimal(18, 2)")]
permet à Entity Framework Core de mapper correctementPrice
en devise dans la base de données. Pour plus d’informations, consultez Types de données. - L’attribut [Display] spécifie le nom complet d’un champ. Dans le code précédent,
Release Date
au lieu deReleaseDate
. - l’attribut [DataType] spécifie le type de données (
Date
). Les informations de temps stockées dans le champ ne s’affichent pas.
DataAnnotations est abordé dans le tutoriel suivant.
Accédez à Pages/Movies, puis placez le curseur sur un lien Modifier pour afficher l’URL cible.
Les liens Edit, Details, et Delete sont générés par le Tag Helper d’ancre dans le fichier Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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>
<a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Les Tag Helpers permettent au code côté serveur de participer à la création et au rendu des éléments HTML dans les fichiers Razor.
Dans le code précédent, le Tag Helper d’ancre génère dynamiquement la valeur d’attribut HTML href
dans la page Razor (l’itinéraire est relatif), asp-page
et l’ID de l’itinéraire (asp-route-id
). Pour plus d’informations, consultez Génération d’URL pour Pages.
Utilisez Afficher la Source dans un navigateur pour examiner le balisage généré. Une partie du code HTML généré est affichée ci-dessous :
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Les liens générés dynamiquement transmettent l’ID de film avec une chaîne de requête. Par exemple, ?id=1
dans https://localhost:5001/Movies/Details?id=1
.
Ajouter un modèle d’itinéraire
Mettez à jour les Pages Razor Edit, Details et Delete pour utiliser le modèle de route {id:int}
. Remplacez la directive de chacune de ces pages (@page "{id:int}"
) par @page
. Exécuter l’application, puis affichez le code source.
Le code HTML généré ajoute l’ID à la partie de chemin de l’URL :
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Une requête à la page avec le modèle d’itinéraire {id:int}
qui n’inclut pas l’entier retourne une erreur HTTP 404 (introuvable). Par exemple, https://localhost:5001/Movies/Details
retourne une erreur 404. Pour que l’ID soit facultatif, ajoutez ?
à la contrainte d’itinéraire :
@page "{id:int?}"
Testez le comportement de @page "{id:int?}"
:
- Définissez la directive de page dans
Pages/Movies/Details.cshtml
sur@page "{id:int?}"
. - Définissez un point d’arrêt dans
public async Task<IActionResult> OnGetAsync(int? id)
, dansPages/Movies/Details.cshtml.cs
. - Accédez à
https://localhost:5001/Movies/Details/
.
Avec la directive @page "{id:int}"
, le point d’arrêt n’est jamais atteint. Le moteur de routage retourne l’erreur HTTP 404. Avec @page "{id:int?}"
, la méthode OnGetAsync
retourne NotFound
(HTTP 404) :
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
Passer en revue la gestion des exceptions d’accès concurrentiel
Passez en revue la méthode OnPostAsync
dans le fichier Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
Le code précédent détecte les exceptions d’accès concurrentiel quand un client supprime le film et que l’autre client envoie les modifications apportées au film.
Pour tester le bloc catch
:
- Définissez un point d’arrêt sur
catch (DbUpdateConcurrencyException)
. - Sélectionnez Edit (Modifier) pour un film, apportez des modifications, mais ne cliquez pas sur Save (Enregistrer).
- Dans une autre fenêtre de navigateur, sélectionnez le lien Delete du même film, puis supprimez le film.
- Dans la fenêtre de navigateur précédente, postez les modifications apportées au film.
Dans le code destiné à la production, il est nécessaire de détecter les conflits d’accès concurrentiel. Pour plus d’informations, consultez Gérer les conflits d’accès concurrentiel.
Validation de la publication et de la liaison
Examinez le fichier Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
Quand une requête HTTP GET est effectuée sur la page Movies/Edit, par exemple, https://localhost:5001/Movies/Edit/3
:
- La méthode
OnGetAsync
extrait le film de la base de données et retourne la méthodePage
. - La méthode
Page
restitue la pagePages/Movies/Edit.cshtml
Razor. Le fichierPages/Movies/Edit.cshtml
contient la directive de modèle@model RazorPagesMovie.Pages.Movies.EditModel
, ce qui rend le modèle Movie disponible sur la page. - Le formulaire d’édition s’affiche avec les valeurs relatives au film.
Quand la page Movies/Edit est postée :
Les valeurs de formulaire affichées dans la page sont liées à la propriété
Movie
. L’attribut[BindProperty]
active la liaison de données.[BindProperty] public Movie Movie { get; set; }
S’il y a des erreurs dans l’état du modèle, par exemple, si
ReleaseDate
ne peut pas être converti en date, le formulaire est à nouveau affiché avec les valeurs soumises.S’il n’y a aucune erreur de modèle, le film est enregistré.
Les méthodes HTTP GET dans les pages Razor Index, Create et Delete suivent un modèle similaire. La méthode HTTP POST OnPostAsync
dans la page Razor Create suit un modèle semblable à la méthode OnPostAsync
dans la page Razor Edit.
Étapes suivantes
L’application de gestion des films générée est un bon début, mais la présentation n’est pas idéale. ReleaseDate doit être écrit en deux mots, Release Date.
Mettre le modèle à niveau
Mettez à jour Models/Movie.cs
avec le code mis en évidence suivant :
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models;
public class Movie
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; } = string.Empty;
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
Dans le code précédent :
- L’annotation de données
[Column(TypeName = "decimal(18, 2)")]
permet à Entity Framework Core de mapper correctementPrice
en devise dans la base de données. Pour plus d’informations, consultez Types de données. - L’attribut [Display] spécifie le nom complet d’un champ. Dans le code précédent,
Release Date
au lieu deReleaseDate
. - l’attribut [DataType] spécifie le type de données (
Date
). Les informations de temps stockées dans le champ ne s’affichent pas.
DataAnnotations est abordé dans le tutoriel suivant.
Accédez à Pages/Movies, puis placez le curseur sur un lien Modifier pour afficher l’URL cible.
Les liens Edit, Details, et Delete sont générés par le Tag Helper d’ancre dans le fichier Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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>
<a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Les Tag Helpers permettent au code côté serveur de participer à la création et au rendu des éléments HTML dans les fichiers Razor.
Dans le code précédent, le Tag Helper d’ancre génère dynamiquement la valeur d’attribut HTML href
dans la page Razor (l’itinéraire est relatif), asp-page
et l’ID de l’itinéraire (asp-route-id
). Pour plus d’informations, consultez Génération d’URL pour Pages.
Utilisez Afficher la Source dans un navigateur pour examiner le balisage généré. Une partie du code HTML généré est affichée ci-dessous :
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Les liens générés dynamiquement transmettent l’ID de film avec une chaîne de requête. Par exemple, ?id=1
dans https://localhost:5001/Movies/Details?id=1
.
Ajouter un modèle d’itinéraire
Mettez à jour les Pages Razor Edit, Details et Delete pour utiliser le modèle de route {id:int}
. Remplacez la directive de chacune de ces pages (@page "{id:int}"
) par @page
. Exécuter l’application, puis affichez le code source.
Le code HTML généré ajoute l’ID à la partie de chemin de l’URL :
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Une requête à la page avec le modèle d’itinéraire {id:int}
qui n’inclut pas l’entier retourne une erreur HTTP 404 (introuvable). Par exemple, https://localhost:5001/Movies/Details
retourne une erreur 404. Pour que l’ID soit facultatif, ajoutez ?
à la contrainte d’itinéraire :
@page "{id:int?}"
Testez le comportement de @page "{id:int?}"
:
- Définissez la directive de page dans
Pages/Movies/Details.cshtml
sur@page "{id:int?}"
. - Définissez un point d’arrêt dans
public async Task<IActionResult> OnGetAsync(int? id)
, dansPages/Movies/Details.cshtml.cs
. - Accédez à
https://localhost:5001/Movies/Details/
.
Avec la directive @page "{id:int}"
, le point d’arrêt n’est jamais atteint. Le moteur de routage retourne l’erreur HTTP 404. Avec @page "{id:int?}"
, la méthode OnGetAsync
retourne NotFound
(HTTP 404) :
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
Passer en revue la gestion des exceptions d’accès concurrentiel
Passez en revue la méthode OnPostAsync
dans le fichier Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
Le code précédent détecte les exceptions d’accès concurrentiel quand un client supprime le film et que l’autre client envoie les modifications apportées au film.
Pour tester le bloc catch
:
- Définissez un point d’arrêt sur
catch (DbUpdateConcurrencyException)
. - Sélectionnez Edit (Modifier) pour un film, apportez des modifications, mais ne cliquez pas sur Save (Enregistrer).
- Dans une autre fenêtre de navigateur, sélectionnez le lien Delete du même film, puis supprimez le film.
- Dans la fenêtre de navigateur précédente, postez les modifications apportées au film.
Dans le code destiné à la production, il est nécessaire de détecter les conflits d’accès concurrentiel. Pour plus d’informations, consultez Gérer les conflits d’accès concurrentiel.
Validation de la publication et de la liaison
Examinez le fichier Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
Quand une requête HTTP GET est effectuée sur la page Movies/Edit, par exemple, https://localhost:5001/Movies/Edit/3
:
- La méthode
OnGetAsync
extrait le film de la base de données et retourne la méthodePage
. - La méthode
Page
restitue la pagePages/Movies/Edit.cshtml
Razor. Le fichierPages/Movies/Edit.cshtml
contient la directive de modèle@model RazorPagesMovie.Pages.Movies.EditModel
, ce qui rend le modèle Movie disponible sur la page. - Le formulaire d’édition s’affiche avec les valeurs relatives au film.
Quand la page Movies/Edit est postée :
Les valeurs de formulaire affichées dans la page sont liées à la propriété
Movie
. L’attribut[BindProperty]
active la liaison de données.[BindProperty] public Movie Movie { get; set; }
S’il y a des erreurs dans l’état du modèle, par exemple, si
ReleaseDate
ne peut pas être converti en date, le formulaire est à nouveau affiché avec les valeurs soumises.S’il n’y a aucune erreur de modèle, le film est enregistré.
Les méthodes HTTP GET dans les pages Razor Index, Create et Delete suivent un modèle similaire. La méthode HTTP POST OnPostAsync
dans la page Razor Create suit un modèle semblable à la méthode OnPostAsync
dans la page Razor Edit.
Étapes suivantes
L’application de gestion des films générée est un bon début, mais la présentation n’est pas idéale. ReleaseDate doit être écrit en deux mots, Release Date.
Mettre le modèle à niveau
Mettez à jour Models/Movie.cs
avec le code mis en évidence suivant :
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models;
public class Movie
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; } = string.Empty;
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
Dans le code précédent :
- L’annotation de données
[Column(TypeName = "decimal(18, 2)")]
permet à Entity Framework Core de mapper correctementPrice
en devise dans la base de données. Pour plus d’informations, consultez Types de données. - L’attribut [Display] spécifie le nom complet d’un champ. Dans le code précédent,
Release Date
au lieu deReleaseDate
. - l’attribut [DataType] spécifie le type de données (
Date
). Les informations de temps stockées dans le champ ne s’affichent pas.
DataAnnotations est abordé dans le tutoriel suivant.
Accédez à Pages/Movies, puis placez le curseur sur un lien Modifier pour afficher l’URL cible.
Les liens Edit, Details, et Delete sont générés par le Tag Helper d’ancre dans le fichier Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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>
<a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Les Tag Helpers permettent au code côté serveur de participer à la création et au rendu des éléments HTML dans les fichiers Razor.
Dans le code précédent, le Tag Helper d’ancre génère dynamiquement la valeur d’attribut HTML href
dans la page Razor (l’itinéraire est relatif), asp-page
et l’ID de l’itinéraire (asp-route-id
). Pour plus d’informations, consultez Génération d’URL pour Pages.
Utilisez Afficher la Source dans un navigateur pour examiner le balisage généré. Une partie du code HTML généré est affichée ci-dessous :
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Les liens générés dynamiquement transmettent l’ID de film avec une chaîne de requête. Par exemple, ?id=1
dans https://localhost:5001/Movies/Details?id=1
.
Ajouter un modèle d’itinéraire
Mettez à jour les Pages Razor Edit, Details et Delete pour utiliser le modèle de route {id:int}
. Remplacez la directive de chacune de ces pages (@page "{id:int}"
) par @page
. Exécuter l’application, puis affichez le code source.
Le code HTML généré ajoute l’ID à la partie de chemin de l’URL :
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Une requête à la page avec le modèle d’itinéraire {id:int}
qui n’inclut pas l’entier retourne une erreur HTTP 404 (introuvable). Par exemple, https://localhost:5001/Movies/Details
retourne une erreur 404. Pour que l’ID soit facultatif, ajoutez ?
à la contrainte d’itinéraire :
@page "{id:int?}"
Testez le comportement de @page "{id:int?}"
:
- Définissez la directive de page dans
Pages/Movies/Details.cshtml
sur@page "{id:int?}"
. - Définissez un point d’arrêt dans
public async Task<IActionResult> OnGetAsync(int? id)
, dansPages/Movies/Details.cshtml.cs
. - Accédez à
https://localhost:5001/Movies/Details/
.
Avec la directive @page "{id:int}"
, le point d’arrêt n’est jamais atteint. Le moteur de routage retourne l’erreur HTTP 404. Avec @page "{id:int?}"
, la méthode OnGetAsync
retourne NotFound
(HTTP 404) :
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
Passer en revue la gestion des exceptions d’accès concurrentiel
Passez en revue la méthode OnPostAsync
dans le fichier Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
Le code précédent détecte les exceptions d’accès concurrentiel quand un client supprime le film et que l’autre client envoie les modifications apportées au film.
Pour tester le bloc catch
:
- Définissez un point d’arrêt sur
catch (DbUpdateConcurrencyException)
. - Sélectionnez Edit (Modifier) pour un film, apportez des modifications, mais ne cliquez pas sur Save (Enregistrer).
- Dans une autre fenêtre de navigateur, sélectionnez le lien Delete du même film, puis supprimez le film.
- Dans la fenêtre de navigateur précédente, postez les modifications apportées au film.
Dans le code destiné à la production, il est nécessaire de détecter les conflits d’accès concurrentiel. Pour plus d’informations, consultez Gérer les conflits d’accès concurrentiel.
Validation de la publication et de la liaison
Examinez le fichier Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
Quand une requête HTTP GET est effectuée sur la page Movies/Edit, par exemple, https://localhost:5001/Movies/Edit/3
:
- La méthode
OnGetAsync
extrait le film de la base de données et retourne la méthodePage
. - La méthode
Page
restitue la pagePages/Movies/Edit.cshtml
Razor. Le fichierPages/Movies/Edit.cshtml
contient la directive de modèle@model RazorPagesMovie.Pages.Movies.EditModel
, ce qui rend le modèle Movie disponible sur la page. - Le formulaire d’édition s’affiche avec les valeurs relatives au film.
Quand la page Movies/Edit est postée :
Les valeurs de formulaire affichées dans la page sont liées à la propriété
Movie
. L’attribut[BindProperty]
active la liaison de données.[BindProperty] public Movie Movie { get; set; }
S’il y a des erreurs dans l’état du modèle, par exemple, si
ReleaseDate
ne peut pas être converti en date, le formulaire est à nouveau affiché avec les valeurs soumises.S’il n’y a aucune erreur de modèle, le film est enregistré.
Les méthodes HTTP GET dans les pages Razor Index, Create et Delete suivent un modèle similaire. La méthode HTTP POST OnPostAsync
dans la page Razor Create suit un modèle semblable à la méthode OnPostAsync
dans la page Razor Edit.
Étapes suivantes
L’application de gestion des films générée est un bon début, mais la présentation n’est pas idéale. ReleaseDate doit être écrit en deux mots, Release Date.
Mettre à jour le code généré
Mettez à jour Models/Movie.cs
avec le code mis en évidence suivant :
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; } = string.Empty;
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; } = string.Empty;
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
}
Dans le code précédent :
- L’annotation de données
[Column(TypeName = "decimal(18, 2)")]
permet à Entity Framework Core de mapper correctementPrice
en devise dans la base de données. Pour plus d’informations, consultez Types de données. - L’attribut [Display] spécifie le nom complet d’un champ. Dans le code précédent, « Release Date » au lieu de « ReleaseDate ».
- l’attribut [DataType] spécifie le type de données (
Date
). Les informations de temps stockées dans le champ ne s’affichent pas.
DataAnnotations est abordé dans le tutoriel suivant.
Accédez à Pages/Movies, puis placez le curseur sur un lien Modifier pour afficher l’URL cible.
Les liens Edit, Details, et Delete sont générés par le Tag Helper d’ancre dans le fichier Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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>
<a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Les Tag Helpers permettent au code côté serveur de participer à la création et au rendu des éléments HTML dans les fichiers Razor.
Dans le code précédent, le Tag Helper d’ancre génère dynamiquement la valeur d’attribut HTML href
dans la page Razor (l’itinéraire est relatif), asp-page
et l’ID de l’itinéraire (asp-route-id
). Pour plus d’informations, consultez Génération d’URL pour Pages.
Utilisez Afficher la Source dans un navigateur pour examiner le balisage généré. Une partie du code HTML généré est affichée ci-dessous :
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Les liens générés dynamiquement transmettent l’ID de film avec une chaîne de requête. Par exemple, ?id=1
dans https://localhost:5001/Movies/Details?id=1
.
Ajouter un modèle d’itinéraire
Mettez à jour les Pages Razor Edit, Details et Delete pour utiliser le modèle de route {id:int}
. Remplacez la directive de chacune de ces pages (@page "{id:int}"
) par @page
. Exécuter l’application, puis affichez le code source.
Le code HTML généré ajoute l’ID à la partie de chemin de l’URL :
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Une requête à la page avec le modèle d’itinéraire {id:int}
qui n’inclut pas l’entier retourne une erreur HTTP 404 (introuvable). Par exemple, https://localhost:5001/Movies/Details
retourne une erreur 404. Pour que l’ID soit facultatif, ajoutez ?
à la contrainte d’itinéraire :
@page "{id:int?}"
Testez le comportement de @page "{id:int?}"
:
- Définissez la directive de page dans
Pages/Movies/Details.cshtml
sur@page "{id:int?}"
. - Définissez un point d’arrêt dans
public async Task<IActionResult> OnGetAsync(int? id)
, dansPages/Movies/Details.cshtml.cs
. - Accédez à
https://localhost:5001/Movies/Details/
.
Avec la directive @page "{id:int}"
, le point d’arrêt n’est jamais atteint. Le moteur de routage retourne l’erreur HTTP 404. Avec @page "{id:int?}"
, la méthode OnGetAsync
retourne NotFound
(HTTP 404) :
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
Passer en revue la gestion des exceptions d’accès concurrentiel
Passez en revue la méthode OnPostAsync
dans le fichier Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return (_context.Movie?.Any(e => e.ID == id)).GetValueOrDefault();
}
Le code précédent détecte les exceptions d’accès concurrentiel quand un client supprime le film et que l’autre client envoie les modifications apportées au film. Le code précédent ne détecte pas les conflits qui se produisent quand deux clients ou plus modifient simultanément le même film. Dans ce cas, les modifications effectuées par plusieurs clients sont appliquées dans l’ordre d’appel de SaveChanges
, et les modifications appliquées ultérieurement peuvent remplacer les modifications antérieures par des valeurs obsolètes.
Pour tester le bloc catch
:
- Définissez un point d’arrêt sur
catch (DbUpdateConcurrencyException)
. - Sélectionnez Edit (Modifier) pour un film, apportez des modifications, mais ne cliquez pas sur Save (Enregistrer).
- Dans une autre fenêtre de navigateur, sélectionnez le lien Delete du même film, puis supprimez le film.
- Dans la fenêtre de navigateur précédente, postez les modifications apportées au film.
Le code de production peut souhaiter détecter d’autres conflits d’accès concurrentiel, comme plusieurs clients qui modifient une entité en même temps. Pour plus d’informations, consultez Gérer les conflits d’accès concurrentiel.
Validation de la publication et de la liaison
Examinez le fichier Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return (_context.Movie?.Any(e => e.ID == id)).GetValueOrDefault();
}
Quand une requête HTTP GET est effectuée sur la page Movies/Edit, par exemple, https://localhost:5001/Movies/Edit/3
:
- La méthode
OnGetAsync
extrait le film de la base de données et retourne la méthodePage
. - La méthode
Page
restitue la pagePages/Movies/Edit.cshtml
Razor. Le fichierPages/Movies/Edit.cshtml
contient la directive de modèle@model RazorPagesMovie.Pages.Movies.EditModel
, ce qui rend le modèle Movie disponible sur la page. - Le formulaire d’édition s’affiche avec les valeurs relatives au film.
Quand la page Movies/Edit est postée :
Les valeurs de formulaire affichées dans la page sont liées à la propriété
Movie
. L’attribut[BindProperty]
active la liaison de données.[BindProperty] public Movie Movie { get; set; }
S’il y a des erreurs dans l’état du modèle, par exemple, si
ReleaseDate
ne peut pas être converti en date, le formulaire est à nouveau affiché avec les valeurs soumises.S’il n’y a aucune erreur de modèle, le film est enregistré.
Les méthodes HTTP GET dans les pages Razor Index, Create et Delete suivent un modèle similaire. La méthode HTTP POST OnPostAsync
dans la page Razor Create suit un modèle semblable à la méthode OnPostAsync
dans la page Razor Edit.
Étapes suivantes
L’application de gestion des films générée est un bon début, mais la présentation n’est pas idéale. ReleaseDate doit être écrit en deux mots, Release Date.
Mettre à jour le code généré
Ouvrez le fichier Models/Movie.cs
, puis ajoutez les lignes affichées en évidence dans le code suivant :
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
}
Dans le code précédent :
- L’annotation de données
[Column(TypeName = "decimal(18, 2)")]
permet à Entity Framework Core de mapper correctementPrice
en devise dans la base de données. Pour plus d’informations, consultez Types de données. - L’attribut [Display] spécifie le nom complet d’un champ. Dans le code précédent, « Release Date » au lieu de « ReleaseDate ».
- l’attribut [DataType] spécifie le type de données (
Date
). Les informations de temps stockées dans le champ ne s’affichent pas.
DataAnnotations est abordé dans le tutoriel suivant.
Accédez à Pages/Movies, puis placez le curseur sur un lien Modifier pour afficher l’URL cible.
Les liens Edit, Details, et Delete sont générés par le Tag Helper d’ancre dans le fichier Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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>
<a asp-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Les Tag Helpers permettent au code côté serveur de participer à la création et au rendu des éléments HTML dans les fichiers Razor.
Dans le code précédent, le Tag Helper d’ancre génère dynamiquement la valeur d’attribut HTML href
dans la page Razor (l’itinéraire est relatif), asp-page
et l’ID de l’itinéraire (asp-route-id
). Pour plus d’informations, consultez Génération d’URL pour Pages.
Utilisez Afficher la Source dans un navigateur pour examiner le balisage généré. Une partie du code HTML généré est affichée ci-dessous :
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Les liens générés dynamiquement transmettent l’ID de film avec une chaîne de requête. Par exemple, ?id=1
dans https://localhost:5001/Movies/Details?id=1
.
Ajouter un modèle d’itinéraire
Mettez à jour les Pages Razor Edit, Details et Delete pour utiliser le modèle de route {id:int}
. Remplacez la directive de chacune de ces pages (@page "{id:int}"
) par @page
. Exécuter l’application, puis affichez le code source.
Le code HTML généré ajoute l’ID à la partie de chemin de l’URL :
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Une requête à la page avec le modèle d’itinéraire {id:int}
qui n’inclut pas l’entier retourne une erreur HTTP 404 (introuvable). Par exemple, https://localhost:5001/Movies/Details
retourne une erreur 404. Pour que l’ID soit facultatif, ajoutez ?
à la contrainte d’itinéraire :
@page "{id:int?}"
Testez le comportement de @page "{id:int?}"
:
- Définissez la directive de page dans
Pages/Movies/Details.cshtml
sur@page "{id:int?}"
. - Définissez un point d’arrêt dans
public async Task<IActionResult> OnGetAsync(int? id)
, dansPages/Movies/Details.cshtml.cs
. - Accédez à
https://localhost:5001/Movies/Details/
.
Avec la directive @page "{id:int}"
, le point d’arrêt n’est jamais atteint. Le moteur de routage retourne l’erreur HTTP 404. Avec @page "{id:int?}"
, la méthode OnGetAsync
retourne NotFound
(HTTP 404) :
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
Passer en revue la gestion des exceptions d’accès concurrentiel
Passez en revue la méthode OnPostAsync
dans le fichier Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.ID == id);
}
Le code précédent détecte les exceptions d’accès concurrentiel quand un client supprime le film et que l’autre client envoie les modifications apportées au film.
Pour tester le bloc catch
:
- Définissez un point d’arrêt sur
catch (DbUpdateConcurrencyException)
. - Sélectionnez Edit (Modifier) pour un film, apportez des modifications, mais ne cliquez pas sur Save (Enregistrer).
- Dans une autre fenêtre de navigateur, sélectionnez le lien Delete du même film, puis supprimez le film.
- Dans la fenêtre de navigateur précédente, postez les modifications apportées au film.
Dans le code destiné à la production, il est nécessaire de détecter les conflits d’accès concurrentiel. Pour plus d’informations, consultez Gérer les conflits d’accès concurrentiel.
Validation de la publication et de la liaison
Examinez le fichier Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.ID == id);
}
Quand une requête HTTP GET est effectuée sur la page Movies/Edit, par exemple, https://localhost:5001/Movies/Edit/3
:
- La méthode
OnGetAsync
extrait le film de la base de données et retourne la méthodePage
. - La méthode
Page
restitue la pagePages/Movies/Edit.cshtml
Razor. Le fichierPages/Movies/Edit.cshtml
contient la directive de modèle@model RazorPagesMovie.Pages.Movies.EditModel
, ce qui rend le modèle Movie disponible sur la page. - Le formulaire d’édition s’affiche avec les valeurs relatives au film.
Quand la page Movies/Edit est postée :
Les valeurs de formulaire affichées dans la page sont liées à la propriété
Movie
. L’attribut[BindProperty]
active la liaison de données.[BindProperty] public Movie Movie { get; set; }
S’il y a des erreurs dans l’état du modèle, par exemple, si
ReleaseDate
ne peut pas être converti en date, le formulaire est à nouveau affiché avec les valeurs soumises.S’il n’y a aucune erreur de modèle, le film est enregistré.
Les méthodes HTTP GET dans les pages Razor Index, Create et Delete suivent un modèle similaire. La méthode HTTP POST OnPostAsync
dans la page Razor Create suit un modèle semblable à la méthode OnPostAsync
dans la page Razor Edit.