Część 5. Aktualizowanie wygenerowanych stron w aplikacji ASP.NET Core
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ostrzeżenie
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz .NET i .NET Core Support Policy (Zasady obsługi platformy .NET Core). Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.
Aplikacja filmowa z rusztowaniem ma dobry początek, ale prezentacja nie jest idealna. Data wydania powinna mieć dwa wyrazy: Data wydania.
Aktualizowanie modelu
Zaktualizuj Models/Movie.cs
za pomocą następującego wyróżnionego kodu:
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; }
}
W poprzednim kodzie:
- Adnotacja
[Column(TypeName = "decimal(18, 2)")]
danych umożliwia programowi Entity Framework Core poprawne mapowaniaPrice
na walutę w bazie danych. Aby uzyskać więcej informacji, zobacz Typy danych. - Atrybut [Display] określa nazwę wyświetlaną pola. W poprzednim kodzie
Release Date
zamiastReleaseDate
. - Atrybut [DataType] określa typ danych (
Date
). Informacje o czasie przechowywane w polu nie są wyświetlane.
Adnotacje danych zostały omówione w następnym samouczku.
Przejdź do stron/filmów i umieść kursor nad linkiem Edytuj , aby wyświetlić docelowy adres URL.
Linki Edytuj, Szczegóły i Usuń są generowane przez pomocnika tagów Pages/Movies/Index.cshtml
zakotwiczenia w pliku.
@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>
Pomocnicy tagów umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor.
W poprzednim kodzie Pomocnik tagów kotwicy dynamicznie generuje wartość atrybutu HTML href
ze Razor strony (trasa jest względna), asp-page
i identyfikator trasy (asp-route-id
). Aby uzyskać więcej informacji, zobacz Generowanie adresów URL dla stron.
Użyj opcji Wyświetl źródło z przeglądarki, aby sprawdzić wygenerowany znacznik. Poniżej przedstawiono część wygenerowanego kodu HTML:
<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>
Dynamicznie generowane linki przekazują identyfikator filmu z ciągiem zapytania. Na przykład element ?id=1
w pliku https://localhost:5001/Movies/Details?id=1
.
Dodawanie szablonu trasy
Zaktualizuj szablon Edytuj, Szczegóły i Usuń Razor strony, aby użyć szablonu {id:int}
trasy. Zmień dyrektywę strony dla każdej z tych stron z @page
na @page "{id:int}"
. Uruchom aplikację, a następnie wyświetl źródło.
Wygenerowany kod HTML dodaje identyfikator do części ścieżki adresu URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Żądanie do strony z szablonem {id:int}
trasy, który nie zawiera liczby całkowitej, zwraca błąd HTTP 404 (nie znaleziono). Na przykład https://localhost:5001/Movies/Details
zwraca błąd 404. Aby określić identyfikator jako opcjonalny, dołącz kod ?
do ograniczenia trasy:
@page "{id:int?}"
Przetestuj zachowanie elementu @page "{id:int?}"
:
- Ustaw dyrektywę page na
Pages/Movies/Details.cshtml
@page "{id:int?}"
. - Ustaw punkt przerwania w pliku w
public async Task<IActionResult> OnGetAsync(int? id)
plikuPages/Movies/Details.cshtml.cs
. - Przejdź do
https://localhost:5001/Movies/Details/
.
Z dyrektywą @page "{id:int}"
punkt przerwania nigdy nie zostanie trafiony. Aparat routingu zwraca protokół HTTP 404. OnGetAsync
Użycie @page "{id:int?}"
metody zwraca NotFound
metodę (HTTP 404):
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
else
{
Movie = movie;
}
return Page();
}
Przeglądanie obsługi wyjątków współbieżności
Przejrzyj metodę OnPostAsync
Pages/Movies/Edit.cshtml.cs
w pliku:
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);
}
Poprzedni kod wykrywa wyjątki współbieżności, gdy jeden klient usunie film, a drugi klient publikuje zmiany w filmie.
Aby przetestować catch
blok:
- Ustaw punkt przerwania na .
catch (DbUpdateConcurrencyException)
- Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
- W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
- W poprzednim oknie przeglądarki opublikuj zmiany w filmie.
Kod produkcyjny może chcieć wykrywać konflikty współbieżności. Aby uzyskać więcej informacji, zobacz Handle concurrency conflicts (Obsługa konfliktów współbieżności).
Publikowanie i wiązanie przeglądu
Pages/Movies/Edit.cshtml.cs
Sprawdź plik:
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)
{
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);
}
Po wysłaniu żądania HTTP GET na stronę Filmy/Edycja, na przykład https://localhost:5001/Movies/Edit/3
:
- Metoda
OnGetAsync
pobiera film z bazy danych i zwraca metodęPage
. - Metoda
Page
renderujePages/Movies/Edit.cshtml
Razor stronę. PlikPages/Movies/Edit.cshtml
zawiera dyrektywę@model RazorPagesMovie.Pages.Movies.EditModel
modelu , która udostępnia model filmowy na stronie. - Formularz Edycja jest wyświetlany z wartościami z filmu.
Po opublikowaniu strony Filmy/Edycja:
Wartości formularza na stronie są powiązane z właściwością
Movie
. Atrybut[BindProperty]
umożliwia powiązanie modelu.[BindProperty] public Movie Movie { get; set; }
Jeśli na przykład w stanie modelu występują błędy, nie można przekonwertować na datę,
ReleaseDate
formularz jest odtwarzany z przesłanymi wartościami.Jeśli nie ma błędów modelu, film zostanie zapisany.
Metody HTTP GET na stronach Indeks, Tworzenie i Usuwanie Razor są zgodne z podobnym wzorcem. Metoda HTTP POST OnPostAsync
na stronie tworzenia Razor jest zgodna ze wzorcem podobnym do OnPostAsync
metody na stronie Edycji Razor .
Następne kroki
Aplikacja filmowa z rusztowaniem ma dobry początek, ale prezentacja nie jest idealna. Data wydania powinna mieć dwa wyrazy: Data wydania.
Aktualizowanie modelu
Zaktualizuj Models/Movie.cs
za pomocą następującego wyróżnionego kodu:
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; }
}
W poprzednim kodzie:
- Adnotacja
[Column(TypeName = "decimal(18, 2)")]
danych umożliwia programowi Entity Framework Core poprawne mapowaniaPrice
na walutę w bazie danych. Aby uzyskać więcej informacji, zobacz Typy danych. - Atrybut [Display] określa nazwę wyświetlaną pola. W poprzednim kodzie
Release Date
zamiastReleaseDate
. - Atrybut [DataType] określa typ danych (
Date
). Informacje o czasie przechowywane w polu nie są wyświetlane.
Adnotacje danych zostały omówione w następnym samouczku.
Przejdź do stron/filmów i umieść kursor nad linkiem Edytuj , aby wyświetlić docelowy adres URL.
Linki Edytuj, Szczegóły i Usuń są generowane przez pomocnika tagów Pages/Movies/Index.cshtml
zakotwiczenia w pliku.
@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>
Pomocnicy tagów umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor.
W poprzednim kodzie Pomocnik tagów kotwicy dynamicznie generuje wartość atrybutu HTML href
ze Razor strony (trasa jest względna), asp-page
i identyfikator trasy (asp-route-id
). Aby uzyskać więcej informacji, zobacz Generowanie adresów URL dla stron.
Użyj opcji Wyświetl źródło z przeglądarki, aby sprawdzić wygenerowany znacznik. Poniżej przedstawiono część wygenerowanego kodu HTML:
<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>
Dynamicznie generowane linki przekazują identyfikator filmu z ciągiem zapytania. Na przykład element ?id=1
w pliku https://localhost:5001/Movies/Details?id=1
.
Dodawanie szablonu trasy
Zaktualizuj szablon Edytuj, Szczegóły i Usuń Razor strony, aby użyć szablonu {id:int}
trasy. Zmień dyrektywę strony dla każdej z tych stron z @page
na @page "{id:int}"
. Uruchom aplikację, a następnie wyświetl źródło.
Wygenerowany kod HTML dodaje identyfikator do części ścieżki adresu URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Żądanie do strony z szablonem {id:int}
trasy, który nie zawiera liczby całkowitej, zwraca błąd HTTP 404 (nie znaleziono). Na przykład https://localhost:5001/Movies/Details
zwraca błąd 404. Aby określić identyfikator jako opcjonalny, dołącz kod ?
do ograniczenia trasy:
@page "{id:int?}"
Przetestuj zachowanie elementu @page "{id:int?}"
:
- Ustaw dyrektywę page na
Pages/Movies/Details.cshtml
@page "{id:int?}"
. - Ustaw punkt przerwania w pliku w
public async Task<IActionResult> OnGetAsync(int? id)
plikuPages/Movies/Details.cshtml.cs
. - Przejdź do
https://localhost:5001/Movies/Details/
.
Z dyrektywą @page "{id:int}"
punkt przerwania nigdy nie zostanie trafiony. Aparat routingu zwraca protokół HTTP 404. OnGetAsync
Użycie @page "{id:int?}"
metody zwraca NotFound
metodę (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();
}
Przeglądanie obsługi wyjątków współbieżności
Przejrzyj metodę OnPostAsync
Pages/Movies/Edit.cshtml.cs
w pliku:
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);
}
Poprzedni kod wykrywa wyjątki współbieżności, gdy jeden klient usunie film, a drugi klient publikuje zmiany w filmie.
Aby przetestować catch
blok:
- Ustaw punkt przerwania na .
catch (DbUpdateConcurrencyException)
- Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
- W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
- W poprzednim oknie przeglądarki opublikuj zmiany w filmie.
Kod produkcyjny może chcieć wykrywać konflikty współbieżności. Aby uzyskać więcej informacji, zobacz Handle concurrency conflicts (Obsługa konfliktów współbieżności).
Publikowanie i wiązanie przeglądu
Pages/Movies/Edit.cshtml.cs
Sprawdź plik:
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);
}
Po wysłaniu żądania HTTP GET na stronę Filmy/Edycja, na przykład https://localhost:5001/Movies/Edit/3
:
- Metoda
OnGetAsync
pobiera film z bazy danych i zwraca metodęPage
. - Metoda
Page
renderujePages/Movies/Edit.cshtml
Razor stronę. PlikPages/Movies/Edit.cshtml
zawiera dyrektywę@model RazorPagesMovie.Pages.Movies.EditModel
modelu , która udostępnia model filmowy na stronie. - Formularz Edycja jest wyświetlany z wartościami z filmu.
Po opublikowaniu strony Filmy/Edycja:
Wartości formularza na stronie są powiązane z właściwością
Movie
. Atrybut[BindProperty]
umożliwia powiązanie modelu.[BindProperty] public Movie Movie { get; set; }
Jeśli na przykład w stanie modelu występują błędy, nie można przekonwertować na datę,
ReleaseDate
formularz jest odtwarzany z przesłanymi wartościami.Jeśli nie ma błędów modelu, film zostanie zapisany.
Metody HTTP GET na stronach Indeks, Tworzenie i Usuwanie Razor są zgodne z podobnym wzorcem. Metoda HTTP POST OnPostAsync
na stronie tworzenia Razor jest zgodna ze wzorcem podobnym do OnPostAsync
metody na stronie Edycji Razor .
Następne kroki
Aplikacja filmowa z rusztowaniem ma dobry początek, ale prezentacja nie jest idealna. Data wydania powinna mieć dwa wyrazy: Data wydania.
Aktualizowanie modelu
Zaktualizuj Models/Movie.cs
za pomocą następującego wyróżnionego kodu:
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; }
}
W poprzednim kodzie:
- Adnotacja
[Column(TypeName = "decimal(18, 2)")]
danych umożliwia programowi Entity Framework Core poprawne mapowaniaPrice
na walutę w bazie danych. Aby uzyskać więcej informacji, zobacz Typy danych. - Atrybut [Display] określa nazwę wyświetlaną pola. W poprzednim kodzie
Release Date
zamiastReleaseDate
. - Atrybut [DataType] określa typ danych (
Date
). Informacje o czasie przechowywane w polu nie są wyświetlane.
Adnotacje danych zostały omówione w następnym samouczku.
Przejdź do stron/filmów i umieść kursor nad linkiem Edytuj , aby wyświetlić docelowy adres URL.
Linki Edytuj, Szczegóły i Usuń są generowane przez pomocnika tagów Pages/Movies/Index.cshtml
zakotwiczenia w pliku.
@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>
Pomocnicy tagów umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor.
W poprzednim kodzie Pomocnik tagów kotwicy dynamicznie generuje wartość atrybutu HTML href
ze Razor strony (trasa jest względna), asp-page
i identyfikator trasy (asp-route-id
). Aby uzyskać więcej informacji, zobacz Generowanie adresów URL dla stron.
Użyj opcji Wyświetl źródło z przeglądarki, aby sprawdzić wygenerowany znacznik. Poniżej przedstawiono część wygenerowanego kodu HTML:
<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>
Dynamicznie generowane linki przekazują identyfikator filmu z ciągiem zapytania. Na przykład element ?id=1
w pliku https://localhost:5001/Movies/Details?id=1
.
Dodawanie szablonu trasy
Zaktualizuj szablon Edytuj, Szczegóły i Usuń Razor strony, aby użyć szablonu {id:int}
trasy. Zmień dyrektywę strony dla każdej z tych stron z @page
na @page "{id:int}"
. Uruchom aplikację, a następnie wyświetl źródło.
Wygenerowany kod HTML dodaje identyfikator do części ścieżki adresu URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Żądanie do strony z szablonem {id:int}
trasy, który nie zawiera liczby całkowitej, zwraca błąd HTTP 404 (nie znaleziono). Na przykład https://localhost:5001/Movies/Details
zwraca błąd 404. Aby określić identyfikator jako opcjonalny, dołącz kod ?
do ograniczenia trasy:
@page "{id:int?}"
Przetestuj zachowanie elementu @page "{id:int?}"
:
- Ustaw dyrektywę page na
Pages/Movies/Details.cshtml
@page "{id:int?}"
. - Ustaw punkt przerwania w pliku w
public async Task<IActionResult> OnGetAsync(int? id)
plikuPages/Movies/Details.cshtml.cs
. - Przejdź do
https://localhost:5001/Movies/Details/
.
Z dyrektywą @page "{id:int}"
punkt przerwania nigdy nie zostanie trafiony. Aparat routingu zwraca protokół HTTP 404. OnGetAsync
Użycie @page "{id:int?}"
metody zwraca NotFound
metodę (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();
}
Przeglądanie obsługi wyjątków współbieżności
Przejrzyj metodę OnPostAsync
Pages/Movies/Edit.cshtml.cs
w pliku:
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);
}
Poprzedni kod wykrywa wyjątki współbieżności, gdy jeden klient usunie film, a drugi klient publikuje zmiany w filmie.
Aby przetestować catch
blok:
- Ustaw punkt przerwania na .
catch (DbUpdateConcurrencyException)
- Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
- W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
- W poprzednim oknie przeglądarki opublikuj zmiany w filmie.
Kod produkcyjny może chcieć wykrywać konflikty współbieżności. Aby uzyskać więcej informacji, zobacz Handle concurrency conflicts (Obsługa konfliktów współbieżności).
Publikowanie i wiązanie przeglądu
Pages/Movies/Edit.cshtml.cs
Sprawdź plik:
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);
}
Po wysłaniu żądania HTTP GET na stronę Filmy/Edycja, na przykład https://localhost:5001/Movies/Edit/3
:
- Metoda
OnGetAsync
pobiera film z bazy danych i zwraca metodęPage
. - Metoda
Page
renderujePages/Movies/Edit.cshtml
Razor stronę. PlikPages/Movies/Edit.cshtml
zawiera dyrektywę@model RazorPagesMovie.Pages.Movies.EditModel
modelu , która udostępnia model filmowy na stronie. - Formularz Edycja jest wyświetlany z wartościami z filmu.
Po opublikowaniu strony Filmy/Edycja:
Wartości formularza na stronie są powiązane z właściwością
Movie
. Atrybut[BindProperty]
umożliwia powiązanie modelu.[BindProperty] public Movie Movie { get; set; }
Jeśli na przykład w stanie modelu występują błędy, nie można przekonwertować na datę,
ReleaseDate
formularz jest odtwarzany z przesłanymi wartościami.Jeśli nie ma błędów modelu, film zostanie zapisany.
Metody HTTP GET na stronach Indeks, Tworzenie i Usuwanie Razor są zgodne z podobnym wzorcem. Metoda HTTP POST OnPostAsync
na stronie tworzenia Razor jest zgodna ze wzorcem podobnym do OnPostAsync
metody na stronie Edycji Razor .
Następne kroki
Aplikacja filmowa z rusztowaniem ma dobry początek, ale prezentacja nie jest idealna. Data wydania powinna mieć dwa wyrazy: Data wydania.
Aktualizowanie wygenerowanego kodu
Zaktualizuj Models/Movie.cs
za pomocą następującego wyróżnionego kodu:
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; }
}
}
W poprzednim kodzie:
- Adnotacja
[Column(TypeName = "decimal(18, 2)")]
danych umożliwia programowi Entity Framework Core poprawne mapowaniaPrice
na walutę w bazie danych. Aby uzyskać więcej informacji, zobacz Typy danych. - Atrybut [Display] określa nazwę wyświetlaną pola. W poprzednim kodzie "Data wydania" zamiast "Data wydania".
- Atrybut [DataType] określa typ danych (
Date
). Informacje o czasie przechowywane w polu nie są wyświetlane.
Adnotacje danych zostały omówione w następnym samouczku.
Przejdź do stron/filmów i umieść kursor nad linkiem Edytuj , aby wyświetlić docelowy adres URL.
Linki Edytuj, Szczegóły i Usuń są generowane przez pomocnika tagów Pages/Movies/Index.cshtml
zakotwiczenia w pliku.
@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>
Pomocnicy tagów umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor.
W poprzednim kodzie Pomocnik tagów kotwicy dynamicznie generuje wartość atrybutu HTML href
ze Razor strony (trasa jest względna), asp-page
i identyfikator trasy (asp-route-id
). Aby uzyskać więcej informacji, zobacz Generowanie adresów URL dla stron.
Użyj opcji Wyświetl źródło z przeglądarki, aby sprawdzić wygenerowany znacznik. Poniżej przedstawiono część wygenerowanego kodu HTML:
<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>
Dynamicznie generowane linki przekazują identyfikator filmu z ciągiem zapytania. Na przykład element ?id=1
w pliku https://localhost:5001/Movies/Details?id=1
.
Dodawanie szablonu trasy
Zaktualizuj szablon Edytuj, Szczegóły i Usuń Razor strony, aby użyć szablonu {id:int}
trasy. Zmień dyrektywę strony dla każdej z tych stron z @page
na @page "{id:int}"
. Uruchom aplikację, a następnie wyświetl źródło.
Wygenerowany kod HTML dodaje identyfikator do części ścieżki adresu URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Żądanie do strony z szablonem {id:int}
trasy, które nie zawiera liczby całkowitej, zwróci błąd HTTP 404 (nie znaleziono). Na przykład https://localhost:5001/Movies/Details
zostanie zwrócony błąd 404. Aby określić identyfikator jako opcjonalny, dołącz kod ?
do ograniczenia trasy:
@page "{id:int?}"
Przetestuj zachowanie elementu @page "{id:int?}"
:
- Ustaw dyrektywę page na
Pages/Movies/Details.cshtml
@page "{id:int?}"
. - Ustaw punkt przerwania w pliku w
public async Task<IActionResult> OnGetAsync(int? id)
plikuPages/Movies/Details.cshtml.cs
. - Przejdź do
https://localhost:5001/Movies/Details/
.
Z dyrektywą @page "{id:int}"
punkt przerwania nigdy nie zostanie trafiony. Aparat routingu zwraca protokół HTTP 404. OnGetAsync
Użycie @page "{id:int?}"
metody zwraca NotFound
metodę (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();
}
Przeglądanie obsługi wyjątków współbieżności
Przejrzyj metodę OnPostAsync
Pages/Movies/Edit.cshtml.cs
w pliku:
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();
}
Poprzedni kod wykrywa wyjątki współbieżności, gdy jeden klient usunie film, a drugi klient publikuje zmiany w filmie. Poprzedni kod nie wykrywa konfliktów, które występują z powodu co najmniej dwóch klientów edytujących ten sam film jednocześnie. W takim przypadku zmiany przez wielu klientów są stosowane w kolejności SaveChanges
wywoływanej, a zastosowane później zmiany mogą zastąpić wcześniejsze edycje nieaktualnymi wartościami.
Aby przetestować catch
blok:
- Ustaw punkt przerwania na .
catch (DbUpdateConcurrencyException)
- Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
- W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
- W poprzednim oknie przeglądarki opublikuj zmiany w filmie.
Kod produkcyjny może chcieć wykrywać dodatkowe konflikty współbieżności, takie jak wielu klientów edytując jednostkę w tym samym czasie. Aby uzyskać więcej informacji, zobacz Handle concurrency conflicts (Obsługa konfliktów współbieżności).
Publikowanie i wiązanie przeglądu
Pages/Movies/Edit.cshtml.cs
Sprawdź plik:
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();
}
Po wysłaniu żądania HTTP GET na stronę Filmy/Edycja, na przykład https://localhost:5001/Movies/Edit/3
:
- Metoda
OnGetAsync
pobiera film z bazy danych i zwraca metodęPage
. - Metoda
Page
renderujePages/Movies/Edit.cshtml
Razor stronę. PlikPages/Movies/Edit.cshtml
zawiera dyrektywę@model RazorPagesMovie.Pages.Movies.EditModel
modelu , która udostępnia model filmowy na stronie. - Formularz Edycja jest wyświetlany z wartościami z filmu.
Po opublikowaniu strony Filmy/Edycja:
Wartości formularza na stronie są powiązane z właściwością
Movie
. Atrybut[BindProperty]
umożliwia powiązanie modelu.[BindProperty] public Movie Movie { get; set; }
Jeśli na przykład w stanie modelu występują błędy, nie można przekonwertować na datę,
ReleaseDate
formularz jest odtwarzany z przesłanymi wartościami.Jeśli nie ma błędów modelu, film zostanie zapisany.
Metody HTTP GET na stronach Indeks, Tworzenie i Usuwanie Razor są zgodne z podobnym wzorcem. Metoda HTTP POST OnPostAsync
na stronie tworzenia Razor jest zgodna ze wzorcem podobnym do OnPostAsync
metody na stronie Edycji Razor .
Następne kroki
Aplikacja filmowa z rusztowaniem ma dobry początek, ale prezentacja nie jest idealna. Data wydania powinna mieć dwa wyrazy: Data wydania.
Aktualizowanie wygenerowanego kodu
Models/Movie.cs
Otwórz plik i dodaj wyróżnione wiersze pokazane w następującym kodzie:
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; }
}
}
W poprzednim kodzie:
- Adnotacja
[Column(TypeName = "decimal(18, 2)")]
danych umożliwia programowi Entity Framework Core poprawne mapowaniaPrice
na walutę w bazie danych. Aby uzyskać więcej informacji, zobacz Typy danych. - Atrybut [Display] określa nazwę wyświetlaną pola. W poprzednim kodzie "Data wydania" zamiast "Data wydania".
- Atrybut [DataType] określa typ danych (
Date
). Informacje o czasie przechowywane w polu nie są wyświetlane.
Adnotacje danych zostały omówione w następnym samouczku.
Przejdź do stron/filmów i umieść kursor nad linkiem Edytuj , aby wyświetlić docelowy adres URL.
Linki Edytuj, Szczegóły i Usuń są generowane przez pomocnika tagów Pages/Movies/Index.cshtml
zakotwiczenia w pliku.
@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>
Pomocnicy tagów umożliwiają kodowi po stronie serwera uczestniczenie w tworzeniu i renderowaniu elementów HTML w plikach Razor.
W poprzednim kodzie Pomocnik tagów kotwicy dynamicznie generuje wartość atrybutu HTML href
ze Razor strony (trasa jest względna), asp-page
i identyfikator trasy (asp-route-id
). Aby uzyskać więcej informacji, zobacz Generowanie adresów URL dla stron.
Użyj opcji Wyświetl źródło z przeglądarki, aby sprawdzić wygenerowany znacznik. Poniżej przedstawiono część wygenerowanego kodu HTML:
<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>
Dynamicznie generowane linki przekazują identyfikator filmu z ciągiem zapytania. Na przykład element ?id=1
w pliku https://localhost:5001/Movies/Details?id=1
.
Dodawanie szablonu trasy
Zaktualizuj szablon Edytuj, Szczegóły i Usuń Razor strony, aby użyć szablonu {id:int}
trasy. Zmień dyrektywę strony dla każdej z tych stron z @page
na @page "{id:int}"
. Uruchom aplikację, a następnie wyświetl źródło.
Wygenerowany kod HTML dodaje identyfikator do części ścieżki adresu URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Żądanie do strony z szablonem {id:int}
trasy, które nie zawiera liczby całkowitej, zwróci błąd HTTP 404 (nie znaleziono). Na przykład https://localhost:5001/Movies/Details
zostanie zwrócony błąd 404. Aby określić identyfikator jako opcjonalny, dołącz kod ?
do ograniczenia trasy:
@page "{id:int?}"
Przetestuj zachowanie elementu @page "{id:int?}"
:
- Ustaw dyrektywę page na
Pages/Movies/Details.cshtml
@page "{id:int?}"
. - Ustaw punkt przerwania w pliku w
public async Task<IActionResult> OnGetAsync(int? id)
plikuPages/Movies/Details.cshtml.cs
. - Przejdź do
https://localhost:5001/Movies/Details/
.
Z dyrektywą @page "{id:int}"
punkt przerwania nigdy nie zostanie trafiony. Aparat routingu zwraca protokół HTTP 404. OnGetAsync
Użycie @page "{id:int?}"
metody zwraca NotFound
metodę (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();
}
Przeglądanie obsługi wyjątków współbieżności
Przejrzyj metodę OnPostAsync
Pages/Movies/Edit.cshtml.cs
w pliku:
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);
}
Poprzedni kod wykrywa wyjątki współbieżności, gdy jeden klient usunie film, a drugi klient publikuje zmiany w filmie.
Aby przetestować catch
blok:
- Ustaw punkt przerwania na .
catch (DbUpdateConcurrencyException)
- Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
- W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
- W poprzednim oknie przeglądarki opublikuj zmiany w filmie.
Kod produkcyjny może chcieć wykrywać konflikty współbieżności. Aby uzyskać więcej informacji, zobacz Handle concurrency conflicts (Obsługa konfliktów współbieżności).
Publikowanie i wiązanie przeglądu
Pages/Movies/Edit.cshtml.cs
Sprawdź plik:
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);
}
Po wysłaniu żądania HTTP GET na stronę Filmy/Edycja, na przykład https://localhost:5001/Movies/Edit/3
:
- Metoda
OnGetAsync
pobiera film z bazy danych i zwraca metodęPage
. - Metoda
Page
renderujePages/Movies/Edit.cshtml
Razor stronę. PlikPages/Movies/Edit.cshtml
zawiera dyrektywę@model RazorPagesMovie.Pages.Movies.EditModel
modelu , która udostępnia model filmowy na stronie. - Formularz Edycja jest wyświetlany z wartościami z filmu.
Po opublikowaniu strony Filmy/Edycja:
Wartości formularza na stronie są powiązane z właściwością
Movie
. Atrybut[BindProperty]
umożliwia powiązanie modelu.[BindProperty] public Movie Movie { get; set; }
Jeśli na przykład w stanie modelu występują błędy, nie można przekonwertować na datę,
ReleaseDate
formularz jest odtwarzany z przesłanymi wartościami.Jeśli nie ma błędów modelu, film zostanie zapisany.
Metody HTTP GET na stronach Indeks, Tworzenie i Usuwanie Razor są zgodne z podobnym wzorcem. Metoda HTTP POST OnPostAsync
na stronie tworzenia Razor jest zgodna ze wzorcem podobnym do OnPostAsync
metody na stronie Edycji Razor .