第5部分:在 ASP.NET Core 應用程式中更新產生的頁面

作者:Rick Anderson

Scaffolded 電影應用程式是一個不錯的起點,但其呈現效果卻不理想。 ReleaseDate 應該是兩個單字,也就是 發行日期

在 Chrome 中開啟的電影應用程式

更新產生的程式碼

以下列醒目提示的程式碼更新 模型/Movie

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

在先前的程式碼中:

  • [Column(TypeName = "decimal(18, 2)")] 資料註解可讓 Entity Framework Core 將 Price 正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型
  • [Display]屬性指定欄位的顯示名稱。 在上述程式碼中,[發行日期],而不是 "ReleaseDate"。
  • [DataType]屬性指定資料 (的類型 Date) 。 不會顯示儲存在欄位中的時間資訊。

接下來的教學課程會涵蓋 DataAnnotations

流覽至 頁面/影片 ,並將滑鼠停留在 編輯 連結上,以查看目標 URL。

滑鼠停留在 Edit 連結並顯示 https://localhost:1234/Movies/Edit/5 的 Url 的瀏覽器視窗

[編輯]、[詳細資料] 和 [刪除] 連結是由 Pages/電影/ Index cshtml 檔案中的 錨點標記協助程式所產生。

@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

標記 協助程式可讓伺服器端程式碼參與建立和轉譯檔案中的 HTML 元素 Razor 。

在上述程式碼中, 錨點 標籤協助程式會以動態方式 href 從頁面產生 HTML 屬性值 Razor (路由是相對) 、 asp-page 和路由識別碼 (asp-route-id) 。 如需詳細資訊,請參閱 頁面的 URL 產生

使用瀏覽器的 視圖來源 來檢查產生的標記。 產生的 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>

動態產生的連結會傳遞含有查詢字串的電影識別碼。 例如, ?id=1 在中 https://localhost:5001/Movies/Details?id=1

新增路由範本

更新 [編輯]、[詳細資料] 和 [刪除] Razor 頁面,以使用 {id:int} 路由範本。 將這些頁面每一頁的頁面指示詞從 @page 變更為 @page "{id:int}"。 執行應用程式,然後檢視原始檔。

產生的 HTML 將識別碼新增至 URL 的路徑部分:

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

具有不 {id:int} 包含整數之路由範本的頁面要求, 將會傳回 HTTP 404 (找不到) 錯誤。 例如,https://localhost:5001/Movies/Details 會傳回 404 錯誤。 若要使識別碼成為選擇性,請將 ? 附加至路由條件約束:

@page "{id:int?}"

測試 @page "{id:int?}" 下列行為:

  1. Pages/Movies/Details.cshtml 中的頁面指示詞設定為 @page "{id:int?}"
  2. 在 [ public async Task<IActionResult> OnGetAsync(int? id) 頁面/電影/詳細資料] 中,設定中斷點。
  3. 瀏覽至 https://localhost:5001/Movies/Details/

使用 @page "{id:int}" 指示詞,永遠不會叫用中斷點。 路由引擎會傳回 HTTP 404。 使用時 @page "{id:int?}" ,此方法會傳回 OnGetAsync 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();
}

檢閱並行存取例外狀況處理

Pages/Movies/Edit.cshtml.cs 檔案中檢閱 OnPostAsync 方法:

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

先前的程式碼會在一個用戶端刪除電影,而其他用戶端將變更張貼到電影時,偵測並行例外狀況。

若要測試 catch 區段:

  1. 在上設定中斷點 catch (DbUpdateConcurrencyException)
  2. 針對電影選取 [編輯],進行變更,但不要輸入 [儲存]。
  3. 在另一個瀏覽器視窗中,選取相同電影的 Delete 連結,然後刪除電影。
  4. 在先前的瀏覽器視窗中,發佈對電影的變更。

實際執行程式碼可能需要偵測並行存取衝突。 如需詳細資訊,請參閱處理並行存取衝突

發佈和繫結檢閱內容

檢查 Pages/Movies/Edit.cshtml.cs 檔案:

public class EditModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    [BindProperty]
    public Movie Movie { get; set; }

    public async Task<IActionResult> OnGetAsync(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);

        if (Movie == null)
        {
            return NotFound();
        }
        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Attach(Movie).State = EntityState.Modified;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(Movie.ID))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return RedirectToPage("./Index");
    }

    private bool MovieExists(int id)
    {
        return _context.Movie.Any(e => e.ID == id);
    }

當對電影/編輯頁面提出 HTTP GET 要求時,例如 https://localhost:5001/Movies/Edit/3

  • OnGetAsync 方法會從資料庫擷取電影,並傳回 Page 方法。
  • 方法會轉譯 Page 頁面/電影/編輯的 cshtml Razor 頁面。 Pages/movie/Edit. cshtml 檔案包含模型指示詞 @model RazorPagesMovie.Pages.Movies.EditModel ,可讓電影模型可在頁面上使用。
  • Edit 表單會顯示來自電影的值。

發佈 Movies/Edit 頁面時:

  • 頁面上的表單值會繫結至 Movie 屬性。 [BindProperty] 屬性可讓模型繫結

    [BindProperty]
    public Movie Movie { get; set; }
    
  • 如果模型狀態中有錯誤(例如, ReleaseDate 無法轉換為日期),則會以提交的值重新顯示表單。

  • 如果沒有任何模型錯誤,則會儲存電影。

Index、Create 和 Delete 頁面中的 HTTP GET 方法會 Razor 遵循類似的模式。 在 [建立] 頁面中的 HTTP POST 方法,會 OnPostAsync Razor 遵循 OnPostAsync [編輯] 頁面中方法的類似模式 Razor 。

其他資源

Scaffolded 電影應用程式是一個不錯的起點,但其呈現效果卻不理想。 ReleaseDate 應該是兩個單字,也就是 發行日期

在 Chrome 中開啟的電影應用程式

更新產生的程式碼

開啟 Models/Movie.cs 檔案,然後新增下列程式碼中顯示的醒目提示行:

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

在先前的程式碼中:

  • [Column(TypeName = "decimal(18, 2)")] 資料註解可讓 Entity Framework Core 將 Price 正確對應到資料庫中的貨幣。 如需詳細資訊,請參閱資料類型
  • [Display]屬性指定欄位的顯示名稱。 在上述程式碼中,[發行日期],而不是 "ReleaseDate"。
  • [DataType]屬性指定資料 (的類型 Date) 。 不會顯示儲存在欄位中的時間資訊。

接下來的教學課程會涵蓋 DataAnnotations

流覽至 頁面/影片 ,並將滑鼠停留在 編輯 連結上,以查看目標 URL。

滑鼠停留在 Edit 連結並顯示 https://localhost:1234/Movies/Edit/5 的 Url 的瀏覽器視窗

[編輯]、[詳細資料] 和 [刪除] 連結是由 Pages/電影/ Index cshtml 檔案中的 錨點標記協助程式所產生。

@foreach (var item in Model.Movie) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

標記 協助程式可讓伺服器端程式碼參與建立和轉譯檔案中的 HTML 元素 Razor 。

在上述程式碼中, 錨點 標籤協助程式會以動態方式 href 從頁面產生 HTML 屬性值 Razor (路由是相對) 、 asp-page 和路由識別碼 (asp-route-id) 。 如需詳細資訊,請參閱 頁面的 URL 產生

使用瀏覽器的 視圖來源 來檢查產生的標記。 產生的 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>

動態產生的連結會傳遞含有查詢字串的電影識別碼。 例如, ?id=1 在中 https://localhost:5001/Movies/Details?id=1

新增路由範本

更新 [編輯]、[詳細資料] 和 [刪除] Razor 頁面,以使用 {id:int} 路由範本。 將這些頁面每一頁的頁面指示詞從 @page 變更為 @page "{id:int}"。 執行應用程式,然後檢視原始檔。

產生的 HTML 將識別碼新增至 URL 的路徑部分:

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

具有不 {id:int} 包含整數之路由範本的頁面要求, 將會傳回 HTTP 404 (找不到) 錯誤。 例如,https://localhost:5001/Movies/Details 會傳回 404 錯誤。 若要使識別碼成為選擇性,請將 ? 附加至路由條件約束:

@page "{id:int?}"

測試 @page "{id:int?}" 下列行為:

  1. Pages/Movies/Details.cshtml 中的頁面指示詞設定為 @page "{id:int?}"
  2. 在 [ public async Task<IActionResult> OnGetAsync(int? id) 頁面/電影/詳細資料] 中,設定中斷點。
  3. 瀏覽至 https://localhost:5001/Movies/Details/

使用 @page "{id:int}" 指示詞,永遠不會叫用中斷點。 路由引擎會傳回 HTTP 404。 使用時 @page "{id:int?}" ,此方法會傳回 OnGetAsync 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();
}

檢閱並行存取例外狀況處理

Pages/Movies/Edit.cshtml.cs 檔案中檢閱 OnPostAsync 方法:

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

先前的程式碼會在一個用戶端刪除電影,而其他用戶端將變更張貼到電影時,偵測並行例外狀況。

若要測試 catch 區段:

  1. 在上設定中斷點 catch (DbUpdateConcurrencyException)
  2. 針對電影選取 [編輯],進行變更,但不要輸入 [儲存]。
  3. 在另一個瀏覽器視窗中,選取相同電影的 Delete 連結,然後刪除電影。
  4. 在先前的瀏覽器視窗中,發佈對電影的變更。

實際執行程式碼可能需要偵測並行存取衝突。 如需詳細資訊,請參閱處理並行存取衝突

發佈和繫結檢閱內容

檢查 Pages/Movies/Edit.cshtml.cs 檔案:

public class EditModel : PageModel
{
    private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;

    public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
    {
        _context = context;
    }

    [BindProperty]
    public Movie Movie { get; set; }

    public async Task<IActionResult> OnGetAsync(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }

        Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);

        if (Movie == null)
        {
            return NotFound();
        }
        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Attach(Movie).State = EntityState.Modified;

        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(Movie.ID))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return RedirectToPage("./Index");
    }

    private bool MovieExists(int id)
    {
        return _context.Movie.Any(e => e.ID == id);
    }

當對電影/編輯頁面提出 HTTP GET 要求時,例如 https://localhost:5001/Movies/Edit/3

  • OnGetAsync 方法會從資料庫擷取電影,並傳回 Page 方法。
  • 方法會轉譯 Page 頁面/電影/編輯的 cshtml Razor 頁面。 Pages/movie/Edit. cshtml 檔案包含模型指示詞 @model RazorPagesMovie.Pages.Movies.EditModel ,可讓電影模型可在頁面上使用。
  • Edit 表單會顯示來自電影的值。

發佈 Movies/Edit 頁面時:

  • 頁面上的表單值會繫結至 Movie 屬性。 [BindProperty] 屬性可讓模型繫結

    [BindProperty]
    public Movie Movie { get; set; }
    
  • 如果模型狀態中有錯誤(例如, ReleaseDate 無法轉換為日期),則會以提交的值重新顯示表單。

  • 如果沒有任何模型錯誤,則會儲存電影。

Index、Create 和 Delete 頁面中的 HTTP GET 方法會 Razor 遵循類似的模式。 在 [建立] 頁面中的 HTTP POST 方法,會 OnPostAsync Razor 遵循 OnPostAsync [編輯] 頁面中方法的類似模式 Razor 。

其他資源