Udostępnij za pośrednictwem


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.

Aplikacja filmowa otwarta w przeglądarce Chrome

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 mapowania Price 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 zamiast ReleaseDate.
  • 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.

Zostanie wyświetlone okno przeglądarki z myszą nad linkiem Edytuj i linkiem adresu URL https://localhost:1234/Movies/Edit/5

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-pagei 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?}":

  1. Ustaw dyrektywę page na Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Ustaw punkt przerwania w pliku w public async Task<IActionResult> OnGetAsync(int? id)pliku Pages/Movies/Details.cshtml.cs.
  3. 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:

  1. Ustaw punkt przerwania na .catch (DbUpdateConcurrencyException)
  2. Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
  3. W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
  4. 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 renderuje Pages/Movies/Edit.cshtmlRazor stronę. Plik Pages/Movies/Edit.cshtml zawiera dyrektywę @model RazorPagesMovie.Pages.Movies.EditModelmodelu , 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.

Aplikacja filmowa otwarta w przeglądarce Chrome

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 mapowania Price 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 zamiast ReleaseDate.
  • 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.

Zostanie wyświetlone okno przeglądarki z myszą nad linkiem Edytuj i linkiem adresu URL https://localhost:1234/Movies/Edit/5

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-pagei 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?}":

  1. Ustaw dyrektywę page na Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Ustaw punkt przerwania w pliku w public async Task<IActionResult> OnGetAsync(int? id)pliku Pages/Movies/Details.cshtml.cs.
  3. 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:

  1. Ustaw punkt przerwania na .catch (DbUpdateConcurrencyException)
  2. Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
  3. W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
  4. 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 renderuje Pages/Movies/Edit.cshtmlRazor stronę. Plik Pages/Movies/Edit.cshtml zawiera dyrektywę @model RazorPagesMovie.Pages.Movies.EditModelmodelu , 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.

Aplikacja filmowa otwarta w przeglądarce Chrome

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 mapowania Price 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 zamiast ReleaseDate.
  • 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.

Zostanie wyświetlone okno przeglądarki z myszą nad linkiem Edytuj i linkiem adresu URL https://localhost:1234/Movies/Edit/5

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-pagei 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?}":

  1. Ustaw dyrektywę page na Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Ustaw punkt przerwania w pliku w public async Task<IActionResult> OnGetAsync(int? id)pliku Pages/Movies/Details.cshtml.cs.
  3. 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:

  1. Ustaw punkt przerwania na .catch (DbUpdateConcurrencyException)
  2. Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
  3. W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
  4. 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 renderuje Pages/Movies/Edit.cshtmlRazor stronę. Plik Pages/Movies/Edit.cshtml zawiera dyrektywę @model RazorPagesMovie.Pages.Movies.EditModelmodelu , 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.

Aplikacja filmowa otwarta w przeglądarce Chrome

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 mapowania Price 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.

Zostanie wyświetlone okno przeglądarki z myszą nad linkiem Edytuj i linkiem adresu URL https://localhost:1234/Movies/Edit/5

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-pagei 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?}":

  1. Ustaw dyrektywę page na Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Ustaw punkt przerwania w pliku w public async Task<IActionResult> OnGetAsync(int? id)pliku Pages/Movies/Details.cshtml.cs.
  3. 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:

  1. Ustaw punkt przerwania na .catch (DbUpdateConcurrencyException)
  2. Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
  3. W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
  4. 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 renderuje Pages/Movies/Edit.cshtmlRazor stronę. Plik Pages/Movies/Edit.cshtml zawiera dyrektywę @model RazorPagesMovie.Pages.Movies.EditModelmodelu , 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.

Aplikacja filmowa otwarta w przeglądarce Chrome

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 mapowania Price 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.

Zostanie wyświetlone okno przeglądarki z myszą nad linkiem Edytuj i linkiem adresu URL https://localhost:1234/Movies/Edit/5

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-pagei 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?}":

  1. Ustaw dyrektywę page na Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Ustaw punkt przerwania w pliku w public async Task<IActionResult> OnGetAsync(int? id)pliku Pages/Movies/Details.cshtml.cs.
  3. 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:

  1. Ustaw punkt przerwania na .catch (DbUpdateConcurrencyException)
  2. Wybierz pozycję Edytuj dla filmu, wprowadź zmiany, ale nie wprowadź pozycji Zapisz.
  3. W innym oknie przeglądarki wybierz link Usuń dla tego samego filmu, a następnie usuń film.
  4. 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 renderuje Pages/Movies/Edit.cshtmlRazor stronę. Plik Pages/Movies/Edit.cshtml zawiera dyrektywę @model RazorPagesMovie.Pages.Movies.EditModelmodelu , 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