Bagian 5, perbarui halaman yang dihasilkan di aplikasi ASP.NET Core

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Aplikasi film perancah memiliki awal yang baik, tetapi presentasinya tidak ideal. ReleaseDate harus dua kata, Tanggal Rilis.

Aplikasi film terbuka di Chrome

Memperbarui model

Perbarui Models/Movie.cs dengan kode yang disorot berikut:

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; }
}

Dalam kode sebelumnya:

  • Anotasi [Column(TypeName = "decimal(18, 2)")] data memungkinkan Entity Framework Core memetakan Price mata uang dengan benar dalam database. Untuk informasi selengkapnya, lihat Jenis Data.
  • Atribut [Display] menentukan nama tampilan bidang. Dalam kode sebelumnya, Release Date alih-alih ReleaseDate.
  • Atribut [DataType] menentukan jenis data (Date). Informasi waktu yang disimpan di bidang tidak ditampilkan.

DataAnnotations dibahas dalam tutorial berikutnya.

Telusuri ke Halaman/Film dan arahkan mouse ke atas tautan Edit untuk melihat URL target.

Jendela browser dengan mouse di atas tautan Edit dan Url https://localhost:1234/Movies/Edit/5 tautan ditampilkan

Tautan Edit, Detail, dan Hapus dihasilkan oleh Pembantu Tag Jangkar dalam Pages/Movies/Index.cshtml file.

@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>

Pembantu Tag mengaktifkan kode sisi server untuk berpartisipasi dalam membuat dan merender elemen HTML dalam file Razor.

Dalam kode sebelumnya, Pembantu Tag Jangkar secara dinamis menghasilkan nilai atribut HTML href dari Razor Halaman (rute relatif), asp-page, dan pengidentifikasi rute (asp-route-id). Untuk informasi selengkapnya, lihat Pembuatan URL untuk Halaman.

Gunakan Tampilkan Sumber dari browser untuk memeriksa markup yang dihasilkan. Sebagian HTML yang dihasilkan ditunjukkan di bawah ini:

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

Tautan yang dihasilkan secara dinamis meneruskan ID film dengan string kueri. Misalnya, dalam ?id=1https://localhost:5001/Movies/Details?id=1.

Menambahkan templat rute

Perbarui Edit, Detail, dan Hapus Razor Halaman untuk menggunakan {id:int} templat rute. Ubah arahan halaman untuk setiap halaman ini dari @page ke @page "{id:int}". Jalankan aplikasi lalu lihat sumber.

HTML yang dihasilkan menambahkan ID ke bagian jalur URL:

<td>
  <a href="/Movies/Edit/1">Edit</a> |
  <a href="/Movies/Details/1">Details</a> |
  <a href="/Movies/Delete/1">Delete</a>
</td>

Permintaan ke halaman dengan {id:int} templat rute yang tidak menyertakan bilangan bulat mengembalikan kesalahan HTTP 404 (tidak ditemukan). Misalnya, https://localhost:5001/Movies/Details mengembalikan kesalahan 404. Untuk menjadikan ID bersifat opsional, tambahkan ? ke batasan rute:

@page "{id:int?}"

Uji perilaku @page "{id:int?}":

  1. Atur direktif Pages/Movies/Details.cshtml halaman ke @page "{id:int?}".
  2. Atur titik henti di public async Task<IActionResult> OnGetAsync(int? id), di Pages/Movies/Details.cshtml.cs.
  3. Buka https://localhost:5001/Movies/Details/.

Dengan direktif @page "{id:int}" , titik henti tidak pernah tertembak. Mesin perutean mengembalikan HTTP 404. Menggunakan @page "{id:int?}", OnGetAsync metode mengembalikan 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();
}

Meninjau penanganan pengecualian konkurensi

OnPostAsync Tinjau metode dalam Pages/Movies/Edit.cshtml.cs file:

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);
}

Kode sebelumnya mendeteksi pengecualian konkurensi ketika satu klien menghapus film dan klien lain memposting perubahan pada film.

Untuk menguji catch blok:

  1. Atur titik henti pada catch (DbUpdateConcurrencyException).
  2. Pilih Edit untuk film, buat perubahan, tetapi jangan masukkan Simpan.
  3. Di jendela browser lain, pilih tautan Hapus untuk film yang sama, lalu hapus film.
  4. Di jendela browser sebelumnya, posting perubahan pada film.

Kode produksi mungkin ingin mendeteksi konflik konkurensi. Lihat Menangani konflik konkurensi untuk informasi selengkapnya.

Memposting dan mengikat ulasan

Periksa Pages/Movies/Edit.cshtml.cs file:

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);
    }

Ketika permintaan HTTP GET dibuat ke halaman Film/Edit, misalnya, https://localhost:5001/Movies/Edit/3:

  • Metode OnGetAsync mengambil film dari database dan mengembalikan Page metode .
  • Metode merender PagePages/Movies/Edit.cshtmlRazor Halaman. File Pages/Movies/Edit.cshtml berisi direktif @model RazorPagesMovie.Pages.Movies.EditModelmodel , yang membuat model film tersedia di halaman.
  • Formulir Edit ditampilkan dengan nilai dari film.

Saat halaman Film/Edit diposting:

  • Nilai formulir pada halaman terikat ke Movie properti . Atribut [BindProperty] mengaktifkan pengikatan Model.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Jika ada kesalahan dalam status model, misalnya, ReleaseDate tidak dapat dikonversi ke tanggal, formulir diputar ulang dengan nilai yang dikirimkan.

  • Jika tidak ada kesalahan model, film disimpan.

Metode HTTP GET di halaman Indeks, Buat, dan Hapus Razor mengikuti pola serupa. Metode HTTP POST OnPostAsync di Halaman Buat Razor mengikuti pola OnPostAsync yang mirip dengan metode di Edit Razor Halaman.

Langkah berikutnya

Aplikasi film perancah memiliki awal yang baik, tetapi presentasinya tidak ideal. ReleaseDate harus dua kata, Tanggal Rilis.

Aplikasi film terbuka di Chrome

Memperbarui model

Perbarui Models/Movie.cs dengan kode yang disorot berikut:

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; }
}

Dalam kode sebelumnya:

  • Anotasi [Column(TypeName = "decimal(18, 2)")] data memungkinkan Entity Framework Core memetakan Price mata uang dengan benar dalam database. Untuk informasi selengkapnya, lihat Jenis Data.
  • Atribut [Display] menentukan nama tampilan bidang. Dalam kode sebelumnya, Release Date alih-alih ReleaseDate.
  • Atribut [DataType] menentukan jenis data (Date). Informasi waktu yang disimpan di bidang tidak ditampilkan.

DataAnnotations dibahas dalam tutorial berikutnya.

Telusuri ke Halaman/Film dan arahkan mouse ke atas tautan Edit untuk melihat URL target.

Jendela browser dengan mouse di atas tautan Edit dan Url https://localhost:1234/Movies/Edit/5 tautan ditampilkan

Tautan Edit, Detail, dan Hapus dihasilkan oleh Pembantu Tag Jangkar dalam Pages/Movies/Index.cshtml file.

@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>

Pembantu Tag mengaktifkan kode sisi server untuk berpartisipasi dalam membuat dan merender elemen HTML dalam file Razor.

Dalam kode sebelumnya, Pembantu Tag Jangkar secara dinamis menghasilkan nilai atribut HTML href dari Razor Halaman (rute relatif), asp-page, dan pengidentifikasi rute (asp-route-id). Untuk informasi selengkapnya, lihat Pembuatan URL untuk Halaman.

Gunakan Tampilkan Sumber dari browser untuk memeriksa markup yang dihasilkan. Sebagian HTML yang dihasilkan ditunjukkan di bawah ini:

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

Tautan yang dihasilkan secara dinamis meneruskan ID film dengan string kueri. Misalnya, dalam ?id=1https://localhost:5001/Movies/Details?id=1.

Menambahkan templat rute

Perbarui Edit, Detail, dan Hapus Razor Halaman untuk menggunakan {id:int} templat rute. Ubah arahan halaman untuk setiap halaman ini dari @page ke @page "{id:int}". Jalankan aplikasi lalu lihat sumber.

HTML yang dihasilkan menambahkan ID ke bagian jalur URL:

<td>
  <a href="/Movies/Edit/1">Edit</a> |
  <a href="/Movies/Details/1">Details</a> |
  <a href="/Movies/Delete/1">Delete</a>
</td>

Permintaan ke halaman dengan {id:int} templat rute yang tidak menyertakan bilangan bulat mengembalikan kesalahan HTTP 404 (tidak ditemukan). Misalnya, https://localhost:5001/Movies/Details mengembalikan kesalahan 404. Untuk menjadikan ID bersifat opsional, tambahkan ? ke batasan rute:

@page "{id:int?}"

Uji perilaku @page "{id:int?}":

  1. Atur direktif Pages/Movies/Details.cshtml halaman ke @page "{id:int?}".
  2. Atur titik henti di public async Task<IActionResult> OnGetAsync(int? id), di Pages/Movies/Details.cshtml.cs.
  3. Buka https://localhost:5001/Movies/Details/.

Dengan direktif @page "{id:int}" , titik henti tidak pernah tertembak. Mesin perutean mengembalikan HTTP 404. Menggunakan @page "{id:int?}", OnGetAsync metode mengembalikan 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();
}

Meninjau penanganan pengecualian konkurensi

OnPostAsync Tinjau metode dalam Pages/Movies/Edit.cshtml.cs file:

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);
}

Kode sebelumnya mendeteksi pengecualian konkurensi ketika satu klien menghapus film dan klien lain memposting perubahan pada film.

Untuk menguji catch blok:

  1. Atur titik henti pada catch (DbUpdateConcurrencyException).
  2. Pilih Edit untuk film, buat perubahan, tetapi jangan masukkan Simpan.
  3. Di jendela browser lain, pilih tautan Hapus untuk film yang sama, lalu hapus film.
  4. Di jendela browser sebelumnya, posting perubahan pada film.

Kode produksi mungkin ingin mendeteksi konflik konkurensi. Lihat Menangani konflik konkurensi untuk informasi selengkapnya.

Memposting dan mengikat ulasan

Periksa Pages/Movies/Edit.cshtml.cs file:

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);
    }

Ketika permintaan HTTP GET dibuat ke halaman Film/Edit, misalnya, https://localhost:5001/Movies/Edit/3:

  • Metode OnGetAsync mengambil film dari database dan mengembalikan Page metode .
  • Metode merender PagePages/Movies/Edit.cshtmlRazor Halaman. File Pages/Movies/Edit.cshtml berisi direktif @model RazorPagesMovie.Pages.Movies.EditModelmodel , yang membuat model film tersedia di halaman.
  • Formulir Edit ditampilkan dengan nilai dari film.

Saat halaman Film/Edit diposting:

  • Nilai formulir pada halaman terikat ke Movie properti . Atribut [BindProperty] mengaktifkan pengikatan Model.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Jika ada kesalahan dalam status model, misalnya, ReleaseDate tidak dapat dikonversi ke tanggal, formulir diputar ulang dengan nilai yang dikirimkan.

  • Jika tidak ada kesalahan model, film disimpan.

Metode HTTP GET di halaman Indeks, Buat, dan Hapus Razor mengikuti pola serupa. Metode HTTP POST OnPostAsync di Halaman Buat Razor mengikuti pola OnPostAsync yang mirip dengan metode di Edit Razor Halaman.

Langkah berikutnya

Aplikasi film perancah memiliki awal yang baik, tetapi presentasinya tidak ideal. ReleaseDate harus dua kata, Tanggal Rilis.

Aplikasi film terbuka di Chrome

Memperbarui kode yang dihasilkan

Perbarui Models/Movie.cs dengan kode yang disorot berikut:

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; }
    }
}

Dalam kode sebelumnya:

  • Anotasi [Column(TypeName = "decimal(18, 2)")] data memungkinkan Entity Framework Core memetakan Price mata uang dengan benar dalam database. Untuk informasi selengkapnya, lihat Jenis Data.
  • Atribut [Display] menentukan nama tampilan bidang. Dalam kode sebelumnya, "Tanggal Rilis" alih-alih "ReleaseDate".
  • Atribut [DataType] menentukan jenis data (Date). Informasi waktu yang disimpan di bidang tidak ditampilkan.

DataAnnotations dibahas dalam tutorial berikutnya.

Telusuri ke Halaman/Film dan arahkan mouse ke atas tautan Edit untuk melihat URL target.

Jendela browser dengan mouse di atas tautan Edit dan Url https://localhost:1234/Movies/Edit/5 tautan ditampilkan

Tautan Edit, Detail, dan Hapus dihasilkan oleh Pembantu Tag Jangkar dalam Pages/Movies/Index.cshtml file.

@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>

Pembantu Tag mengaktifkan kode sisi server untuk berpartisipasi dalam membuat dan merender elemen HTML dalam file Razor.

Dalam kode sebelumnya, Pembantu Tag Jangkar secara dinamis menghasilkan nilai atribut HTML href dari Razor Halaman (rute relatif), asp-page, dan pengidentifikasi rute (asp-route-id). Untuk informasi selengkapnya, lihat Pembuatan URL untuk Halaman.

Gunakan Tampilkan Sumber dari browser untuk memeriksa markup yang dihasilkan. Sebagian HTML yang dihasilkan ditunjukkan di bawah ini:

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

Tautan yang dihasilkan secara dinamis meneruskan ID film dengan string kueri. Misalnya, dalam ?id=1https://localhost:5001/Movies/Details?id=1.

Menambahkan templat rute

Perbarui Edit, Detail, dan Hapus Razor Halaman untuk menggunakan {id:int} templat rute. Ubah arahan halaman untuk setiap halaman ini dari @page ke @page "{id:int}". Jalankan aplikasi lalu lihat sumber.

HTML yang dihasilkan menambahkan ID ke bagian jalur URL:

<td>
  <a href="/Movies/Edit/1">Edit</a> |
  <a href="/Movies/Details/1">Details</a> |
  <a href="/Movies/Delete/1">Delete</a>
</td>

Permintaan ke halaman dengan {id:int} templat rute yang tidak menyertakan bilangan bulat akan mengembalikan kesalahan HTTP 404 (tidak ditemukan). Misalnya, https://localhost:5001/Movies/Details akan mengembalikan kesalahan 404. Untuk menjadikan ID bersifat opsional, tambahkan ? ke batasan rute:

@page "{id:int?}"

Uji perilaku @page "{id:int?}":

  1. Atur direktif Pages/Movies/Details.cshtml halaman ke @page "{id:int?}".
  2. Atur titik henti di public async Task<IActionResult> OnGetAsync(int? id), di Pages/Movies/Details.cshtml.cs.
  3. Buka https://localhost:5001/Movies/Details/.

Dengan direktif @page "{id:int}" , titik henti tidak pernah tertembak. Mesin perutean mengembalikan HTTP 404. Menggunakan @page "{id:int?}", OnGetAsync metode mengembalikan 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();
}

Meninjau penanganan pengecualian konkurensi

OnPostAsync Tinjau metode dalam Pages/Movies/Edit.cshtml.cs file:

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();
}

Kode sebelumnya mendeteksi pengecualian konkurensi ketika satu klien menghapus film dan klien lain memposting perubahan pada film. Kode sebelumnya tidak mendeteksi konflik yang terjadi karena dua klien atau lebih yang mengedit film yang sama secara bersamaan. Dalam hal ini pengeditan oleh beberapa klien diterapkan dalam urutan yang SaveChanges dipanggil dan pengeditan yang diterapkan nanti dapat menimpa pengeditan sebelumnya dengan nilai basi.

Untuk menguji catch blok:

  1. Atur titik henti pada catch (DbUpdateConcurrencyException).
  2. Pilih Edit untuk film, buat perubahan, tetapi jangan masukkan Simpan.
  3. Di jendela browser lain, pilih tautan Hapus untuk film yang sama, lalu hapus film.
  4. Di jendela browser sebelumnya, posting perubahan pada film.

Kode produksi mungkin ingin mendeteksi konflik konkurensi tambahan seperti beberapa klien yang mengedit entitas secara bersamaan. Lihat Menangani konflik konkurensi untuk informasi selengkapnya.

Memposting dan mengikat ulasan

Periksa Pages/Movies/Edit.cshtml.cs file:

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();
    }

Ketika permintaan HTTP GET dibuat ke halaman Film/Edit, misalnya, https://localhost:5001/Movies/Edit/3:

  • Metode OnGetAsync mengambil film dari database dan mengembalikan Page metode .
  • Metode merender PagePages/Movies/Edit.cshtmlRazor Halaman. File Pages/Movies/Edit.cshtml berisi direktif @model RazorPagesMovie.Pages.Movies.EditModelmodel , yang membuat model film tersedia di halaman.
  • Formulir Edit ditampilkan dengan nilai dari film.

Saat halaman Film/Edit diposting:

  • Nilai formulir pada halaman terikat ke Movie properti . Atribut [BindProperty] mengaktifkan pengikatan Model.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Jika ada kesalahan dalam status model, misalnya, ReleaseDate tidak dapat dikonversi ke tanggal, formulir diputar ulang dengan nilai yang dikirimkan.

  • Jika tidak ada kesalahan model, film disimpan.

Metode HTTP GET di halaman Indeks, Buat, dan Hapus Razor mengikuti pola serupa. Metode HTTP POST OnPostAsync di Halaman Buat Razor mengikuti pola OnPostAsync yang mirip dengan metode di Edit Razor Halaman.

Langkah berikutnya

Aplikasi film perancah memiliki awal yang baik, tetapi presentasinya tidak ideal. ReleaseDate harus dua kata, Tanggal Rilis.

Aplikasi film terbuka di Chrome

Memperbarui kode yang dihasilkan

Models/Movie.cs Buka file dan tambahkan baris yang disorot yang diperlihatkan dalam kode berikut:

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; }
    }
}

Dalam kode sebelumnya:

  • Anotasi [Column(TypeName = "decimal(18, 2)")] data memungkinkan Entity Framework Core memetakan Price mata uang dengan benar dalam database. Untuk informasi selengkapnya, lihat Jenis Data.
  • Atribut [Display] menentukan nama tampilan bidang. Dalam kode sebelumnya, "Tanggal Rilis" alih-alih "ReleaseDate".
  • Atribut [DataType] menentukan jenis data (Date). Informasi waktu yang disimpan di bidang tidak ditampilkan.

DataAnnotations dibahas dalam tutorial berikutnya.

Telusuri ke Halaman/Film dan arahkan mouse ke atas tautan Edit untuk melihat URL target.

Jendela browser dengan mouse di atas tautan Edit dan Url https://localhost:1234/Movies/Edit/5 tautan ditampilkan

Tautan Edit, Detail, dan Hapus dihasilkan oleh Pembantu Tag Jangkar dalam Pages/Movies/Index.cshtml file.

@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>

Pembantu Tag mengaktifkan kode sisi server untuk berpartisipasi dalam membuat dan merender elemen HTML dalam file Razor.

Dalam kode sebelumnya, Pembantu Tag Jangkar secara dinamis menghasilkan nilai atribut HTML href dari Razor Halaman (rute relatif), asp-page, dan pengidentifikasi rute (asp-route-id). Untuk informasi selengkapnya, lihat Pembuatan URL untuk Halaman.

Gunakan Tampilkan Sumber dari browser untuk memeriksa markup yang dihasilkan. Sebagian HTML yang dihasilkan ditunjukkan di bawah ini:

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

Tautan yang dihasilkan secara dinamis meneruskan ID film dengan string kueri. Misalnya, dalam ?id=1https://localhost:5001/Movies/Details?id=1.

Menambahkan templat rute

Perbarui Edit, Detail, dan Hapus Razor Halaman untuk menggunakan {id:int} templat rute. Ubah arahan halaman untuk setiap halaman ini dari @page ke @page "{id:int}". Jalankan aplikasi lalu lihat sumber.

HTML yang dihasilkan menambahkan ID ke bagian jalur URL:

<td>
  <a href="/Movies/Edit/1">Edit</a> |
  <a href="/Movies/Details/1">Details</a> |
  <a href="/Movies/Delete/1">Delete</a>
</td>

Permintaan ke halaman dengan {id:int} templat rute yang tidak menyertakan bilangan bulat akan mengembalikan kesalahan HTTP 404 (tidak ditemukan). Misalnya, https://localhost:5001/Movies/Details akan mengembalikan kesalahan 404. Untuk menjadikan ID bersifat opsional, tambahkan ? ke batasan rute:

@page "{id:int?}"

Uji perilaku @page "{id:int?}":

  1. Atur direktif Pages/Movies/Details.cshtml halaman ke @page "{id:int?}".
  2. Atur titik henti di public async Task<IActionResult> OnGetAsync(int? id), di Pages/Movies/Details.cshtml.cs.
  3. Buka https://localhost:5001/Movies/Details/.

Dengan direktif @page "{id:int}" , titik henti tidak pernah tertembak. Mesin perutean mengembalikan HTTP 404. Menggunakan @page "{id:int?}", OnGetAsync metode mengembalikan 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();
}

Meninjau penanganan pengecualian konkurensi

OnPostAsync Tinjau metode dalam Pages/Movies/Edit.cshtml.cs file:

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);
}

Kode sebelumnya mendeteksi pengecualian konkurensi ketika satu klien menghapus film dan klien lain memposting perubahan pada film.

Untuk menguji catch blok:

  1. Atur titik henti pada catch (DbUpdateConcurrencyException).
  2. Pilih Edit untuk film, buat perubahan, tetapi jangan masukkan Simpan.
  3. Di jendela browser lain, pilih tautan Hapus untuk film yang sama, lalu hapus film.
  4. Di jendela browser sebelumnya, posting perubahan pada film.

Kode produksi mungkin ingin mendeteksi konflik konkurensi. Lihat Menangani konflik konkurensi untuk informasi selengkapnya.

Memposting dan mengikat ulasan

Periksa Pages/Movies/Edit.cshtml.cs file:

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);
    }

Ketika permintaan HTTP GET dibuat ke halaman Film/Edit, misalnya, https://localhost:5001/Movies/Edit/3:

  • Metode OnGetAsync mengambil film dari database dan mengembalikan Page metode .
  • Metode merender PagePages/Movies/Edit.cshtmlRazor Halaman. File Pages/Movies/Edit.cshtml berisi direktif @model RazorPagesMovie.Pages.Movies.EditModelmodel , yang membuat model film tersedia di halaman.
  • Formulir Edit ditampilkan dengan nilai dari film.

Saat halaman Film/Edit diposting:

  • Nilai formulir pada halaman terikat ke Movie properti . Atribut [BindProperty] mengaktifkan pengikatan Model.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Jika ada kesalahan dalam status model, misalnya, ReleaseDate tidak dapat dikonversi ke tanggal, formulir diputar ulang dengan nilai yang dikirimkan.

  • Jika tidak ada kesalahan model, film disimpan.

Metode HTTP GET di halaman Indeks, Buat, dan Hapus Razor mengikuti pola serupa. Metode HTTP POST OnPostAsync di Halaman Buat Razor mengikuti pola OnPostAsync yang mirip dengan metode di Edit Razor Halaman.

Langkah berikutnya