第 5 部分,更新 ASP.NET Core 應用程式中產生的頁面
注意
這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
Scaffolded 電影應用程式是一個不錯的起點,但其呈現效果卻不理想。 ReleaseDate 應該是 Release Date (兩個字)。
更新模型
使用下列醒目提示的程式碼更新 Models/Movie.cs
:
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] 屬性會指定欄位的顯示名稱。 在上述程式碼中,會使用
Release Date
而不是ReleaseDate
。 - [DataType] 屬性會指定資料的類型 (
Date
)。 不會顯示儲存在欄位中的時間資訊。
接下來的教學課程會涵蓋 DataAnnotations。
瀏覽至 [Pages/Movies],然後將滑鼠停留在 [編輯] 連結,以查看目標 URL。
Pages/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
@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>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。
在上述程式碼中,錨點標籤協助程式href
會從 Razor Page (路由是相對路由)、asp-page
和路由識別碼 (asp-route-id
) 動態產生 HTML 屬性值。 如需詳細資訊,請參閱 Pages 的 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>
動態產生的連結會傳遞含有查詢字串的電影識別碼。 例如,https://localhost:5001/Movies/Details?id=1
中的 ?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?}"
的行為:
- 將
Pages/Movies/Details.cshtml
中的 page 指示詞設定為@page "{id:int?}"
。 - 在
public async Task<IActionResult> OnGetAsync(int? id)
、Pages/Movies/Details.cshtml.cs
中設定中斷點。 - 瀏覽至
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
區段:
- 在
catch (DbUpdateConcurrencyException)
上設定中斷點。 - 針對電影選取 [編輯],進行變更,但不要輸入 [儲存]。
- 在另一個瀏覽器視窗中,選取相同電影的 Delete 連結,然後刪除電影。
- 在先前的瀏覽器視窗中,發佈對電影的變更。
實際執行程式碼可能需要偵測並行存取衝突。 如需詳細資訊,請參閱處理並行存取衝突。
發佈和繫結檢閱內容
檢查 Pages/Movies/Edit.cshtml.cs
檔案:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
對 Movies/Edit 頁面提出 HTTP GET 要求時 (例如,https://localhost:5001/Movies/Edit/3
):
OnGetAsync
方法會從資料庫擷取電影,並傳回Page
方法。Page
方法會轉譯Pages/Movies/Edit.cshtml
Razor Page。Pages/Movies/Edit.cshtml
檔案包含模型指示詞@model RazorPagesMovie.Pages.Movies.EditModel
,這會讓電影模型可以在頁面上使用。- Edit 表單會顯示來自電影的值。
發佈 Movies/Edit 頁面時:
頁面上的表單值會繫結至
Movie
屬性。[BindProperty]
屬性可讓模型繫結。[BindProperty] public Movie Movie { get; set; }
如果模型狀態中有錯誤 (例如
ReleaseDate
無法轉換為日期),則會以提交的值重新顯示表單。如果沒有任何模型錯誤,則會儲存電影。
Index、Create 和 Delete Razor 頁面中的 HTTP GET 方法都會依循類似的模式。 Create Razor Page中的 HTTP POST OnPostAsync
方法,會依循與 Edit Razor 頁面中的 OnPostAsync
方法類似的模式。
下一步
Scaffolded 電影應用程式是一個不錯的起點,但其呈現效果卻不理想。 ReleaseDate 應該是 Release Date (兩個字)。
更新模型
使用下列醒目提示的程式碼更新 Models/Movie.cs
:
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] 屬性會指定欄位的顯示名稱。 在上述程式碼中,會使用
Release Date
而不是ReleaseDate
。 - [DataType] 屬性會指定資料的類型 (
Date
)。 不會顯示儲存在欄位中的時間資訊。
接下來的教學課程會涵蓋 DataAnnotations。
瀏覽至 [Pages/Movies],然後將滑鼠停留在 [編輯] 連結,以查看目標 URL。
Pages/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
@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>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。
在上述程式碼中,錨點標籤協助程式href
會從 Razor Page (路由是相對路由)、asp-page
和路由識別碼 (asp-route-id
) 動態產生 HTML 屬性值。 如需詳細資訊,請參閱 Pages 的 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>
動態產生的連結會傳遞含有查詢字串的電影識別碼。 例如,https://localhost:5001/Movies/Details?id=1
中的 ?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?}"
的行為:
- 將
Pages/Movies/Details.cshtml
中的 page 指示詞設定為@page "{id:int?}"
。 - 在
public async Task<IActionResult> OnGetAsync(int? id)
、Pages/Movies/Details.cshtml.cs
中設定中斷點。 - 瀏覽至
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
區段:
- 在
catch (DbUpdateConcurrencyException)
上設定中斷點。 - 針對電影選取 [編輯],進行變更,但不要輸入 [儲存]。
- 在另一個瀏覽器視窗中,選取相同電影的 Delete 連結,然後刪除電影。
- 在先前的瀏覽器視窗中,發佈對電影的變更。
實際執行程式碼可能需要偵測並行存取衝突。 如需詳細資訊,請參閱處理並行存取衝突。
發佈和繫結檢閱內容
檢查 Pages/Movies/Edit.cshtml.cs
檔案:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
對 Movies/Edit 頁面提出 HTTP GET 要求時 (例如,https://localhost:5001/Movies/Edit/3
):
OnGetAsync
方法會從資料庫擷取電影,並傳回Page
方法。Page
方法會轉譯Pages/Movies/Edit.cshtml
Razor Page。Pages/Movies/Edit.cshtml
檔案包含模型指示詞@model RazorPagesMovie.Pages.Movies.EditModel
,這會讓電影模型可以在頁面上使用。- Edit 表單會顯示來自電影的值。
發佈 Movies/Edit 頁面時:
頁面上的表單值會繫結至
Movie
屬性。[BindProperty]
屬性可讓模型繫結。[BindProperty] public Movie Movie { get; set; }
如果模型狀態中有錯誤 (例如
ReleaseDate
無法轉換為日期),則會以提交的值重新顯示表單。如果沒有任何模型錯誤,則會儲存電影。
Index、Create 和 Delete Razor 頁面中的 HTTP GET 方法都會依循類似的模式。 Create Razor Page中的 HTTP POST OnPostAsync
方法,會依循與 Edit Razor 頁面中的 OnPostAsync
方法類似的模式。
下一步
Scaffolded 電影應用程式是一個不錯的起點,但其呈現效果卻不理想。 ReleaseDate 應該是 Release Date (兩個字)。
更新模型
使用下列醒目提示的程式碼更新 Models/Movie.cs
:
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] 屬性會指定欄位的顯示名稱。 在上述程式碼中,會使用
Release Date
而不是ReleaseDate
。 - [DataType] 屬性會指定資料的類型 (
Date
)。 不會顯示儲存在欄位中的時間資訊。
接下來的教學課程會涵蓋 DataAnnotations。
瀏覽至 [Pages/Movies],然後將滑鼠停留在 [編輯] 連結,以查看目標 URL。
Pages/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
@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>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。
在上述程式碼中,錨點標籤協助程式href
會從 Razor Page (路由是相對路由)、asp-page
和路由識別碼 (asp-route-id
) 動態產生 HTML 屬性值。 如需詳細資訊,請參閱 Pages 的 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>
動態產生的連結會傳遞含有查詢字串的電影識別碼。 例如,https://localhost:5001/Movies/Details?id=1
中的 ?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?}"
的行為:
- 將
Pages/Movies/Details.cshtml
中的 page 指示詞設定為@page "{id:int?}"
。 - 在
public async Task<IActionResult> OnGetAsync(int? id)
、Pages/Movies/Details.cshtml.cs
中設定中斷點。 - 瀏覽至
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
區段:
- 在
catch (DbUpdateConcurrencyException)
上設定中斷點。 - 針對電影選取 [編輯],進行變更,但不要輸入 [儲存]。
- 在另一個瀏覽器視窗中,選取相同電影的 Delete 連結,然後刪除電影。
- 在先前的瀏覽器視窗中,發佈對電影的變更。
實際執行程式碼可能需要偵測並行存取衝突。 如需詳細資訊,請參閱處理並行存取衝突。
發佈和繫結檢閱內容
檢查 Pages/Movies/Edit.cshtml.cs
檔案:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
對 Movies/Edit 頁面提出 HTTP GET 要求時 (例如,https://localhost:5001/Movies/Edit/3
):
OnGetAsync
方法會從資料庫擷取電影,並傳回Page
方法。Page
方法會轉譯Pages/Movies/Edit.cshtml
Razor Page。Pages/Movies/Edit.cshtml
檔案包含模型指示詞@model RazorPagesMovie.Pages.Movies.EditModel
,這會讓電影模型可以在頁面上使用。- Edit 表單會顯示來自電影的值。
發佈 Movies/Edit 頁面時:
頁面上的表單值會繫結至
Movie
屬性。[BindProperty]
屬性可讓模型繫結。[BindProperty] public Movie Movie { get; set; }
如果模型狀態中有錯誤 (例如
ReleaseDate
無法轉換為日期),則會以提交的值重新顯示表單。如果沒有任何模型錯誤,則會儲存電影。
Index、Create 和 Delete Razor 頁面中的 HTTP GET 方法都會依循類似的模式。 Create Razor Page中的 HTTP POST OnPostAsync
方法,會依循與 Edit Razor 頁面中的 OnPostAsync
方法類似的模式。
下一步
Scaffolded 電影應用程式是一個不錯的起點,但其呈現效果卻不理想。 ReleaseDate 應該是 Release Date (兩個字)。
更新產生的程式碼
使用下列醒目提示的程式碼更新 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; } = 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] 屬性會指定欄位的顯示名稱。 在上述程式碼中,應為「Release Date」,而不是「ReleaseDate」。
- [DataType] 屬性會指定資料的類型 (
Date
)。 不會顯示儲存在欄位中的時間資訊。
接下來的教學課程會涵蓋 DataAnnotations。
瀏覽至 [Pages/Movies],然後將滑鼠停留在 [編輯] 連結,以查看目標 URL。
Pages/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
@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>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。
在上述程式碼中,錨點標籤協助程式href
會從 Razor Page (路由是相對路由)、asp-page
和路由識別碼 (asp-route-id
) 動態產生 HTML 屬性值。 如需詳細資訊,請參閱 Pages 的 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>
動態產生的連結會傳遞含有查詢字串的電影識別碼。 例如,https://localhost:5001/Movies/Details?id=1
中的 ?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?}"
的行為:
- 將
Pages/Movies/Details.cshtml
中的 page 指示詞設定為@page "{id:int?}"
。 - 在
public async Task<IActionResult> OnGetAsync(int? id)
、Pages/Movies/Details.cshtml.cs
中設定中斷點。 - 瀏覽至
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)).GetValueOrDefault();
}
當一個用戶端刪除電影,而另一個用戶端發佈對電影的變更時,先前的程式碼會偵測並行存取例外狀況。 先前的程式碼不會偵測因為兩個或多個用戶端同時編輯相同電影而發生的衝突。 在此情況下,多個用戶端的編輯會依呼叫 SaveChanges
的順序套用,後續所套用的編輯可能會覆寫先前帶有過時值的編輯。
若要測試 catch
區段:
- 在
catch (DbUpdateConcurrencyException)
上設定中斷點。 - 針對電影選取 [編輯],進行變更,但不要輸入 [儲存]。
- 在另一個瀏覽器視窗中,選取相同電影的 Delete 連結,然後刪除電影。
- 在先前的瀏覽器視窗中,發佈對電影的變更。
生產程式碼可能會偵測其他並行衝突,例如:同時針對實體進行編輯的多個用戶端。 如需詳細資訊,請參閱處理並行存取衝突。
發佈和繫結檢閱內容
檢查 Pages/Movies/Edit.cshtml.cs
檔案:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return (_context.Movie?.Any(e => e.ID == id)).GetValueOrDefault();
}
對 Movies/Edit 頁面提出 HTTP GET 要求時 (例如,https://localhost:5001/Movies/Edit/3
):
OnGetAsync
方法會從資料庫擷取電影,並傳回Page
方法。Page
方法會轉譯Pages/Movies/Edit.cshtml
Razor Page。Pages/Movies/Edit.cshtml
檔案包含模型指示詞@model RazorPagesMovie.Pages.Movies.EditModel
,這會讓電影模型可以在頁面上使用。- Edit 表單會顯示來自電影的值。
發佈 Movies/Edit 頁面時:
頁面上的表單值會繫結至
Movie
屬性。[BindProperty]
屬性可讓模型繫結。[BindProperty] public Movie Movie { get; set; }
如果模型狀態中有錯誤 (例如
ReleaseDate
無法轉換為日期),則會以提交的值重新顯示表單。如果沒有任何模型錯誤,則會儲存電影。
Index、Create 和 Delete Razor 頁面中的 HTTP GET 方法都會依循類似的模式。 Create Razor Page中的 HTTP POST OnPostAsync
方法,會依循與 Edit Razor 頁面中的 OnPostAsync
方法類似的模式。
下一步
Scaffolded 電影應用程式是一個不錯的起點,但其呈現效果卻不理想。 ReleaseDate 應該是 Release Date (兩個字)。
更新產生的程式碼
開啟 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] 屬性會指定欄位的顯示名稱。 在上述程式碼中,應為「Release Date」,而不是「ReleaseDate」。
- [DataType] 屬性會指定資料的類型 (
Date
)。 不會顯示儲存在欄位中的時間資訊。
接下來的教學課程會涵蓋 DataAnnotations。
瀏覽至 [Pages/Movies],然後將滑鼠停留在 [編輯] 連結,以查看目標 URL。
Pages/Movies/Index.cshtml
檔案中的 Core MVC 錨點標籤協助程式會產生 [編輯]、[詳細資料] 和 [刪除] 連結。
@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>
標記協助程式可啟用伺服器端程式碼,以參與建立和轉譯 Razor 檔案中的 HTML 元素。
在上述程式碼中,錨點標籤協助程式href
會從 Razor Page (路由是相對路由)、asp-page
和路由識別碼 (asp-route-id
) 動態產生 HTML 屬性值。 如需詳細資訊,請參閱 Pages 的 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>
動態產生的連結會傳遞含有查詢字串的電影識別碼。 例如,https://localhost:5001/Movies/Details?id=1
中的 ?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?}"
的行為:
- 將
Pages/Movies/Details.cshtml
中的 page 指示詞設定為@page "{id:int?}"
。 - 在
public async Task<IActionResult> OnGetAsync(int? id)
、Pages/Movies/Details.cshtml.cs
中設定中斷點。 - 瀏覽至
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
區段:
- 在
catch (DbUpdateConcurrencyException)
上設定中斷點。 - 針對電影選取 [編輯],進行變更,但不要輸入 [儲存]。
- 在另一個瀏覽器視窗中,選取相同電影的 Delete 連結,然後刪除電影。
- 在先前的瀏覽器視窗中,發佈對電影的變更。
實際執行程式碼可能需要偵測並行存取衝突。 如需詳細資訊,請參閱處理並行存取衝突。
發佈和繫結檢閱內容
檢查 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);
}
對 Movies/Edit 頁面提出 HTTP GET 要求時 (例如,https://localhost:5001/Movies/Edit/3
):
OnGetAsync
方法會從資料庫擷取電影,並傳回Page
方法。Page
方法會轉譯Pages/Movies/Edit.cshtml
Razor Page。Pages/Movies/Edit.cshtml
檔案包含模型指示詞@model RazorPagesMovie.Pages.Movies.EditModel
,這會讓電影模型可以在頁面上使用。- Edit 表單會顯示來自電影的值。
發佈 Movies/Edit 頁面時:
頁面上的表單值會繫結至
Movie
屬性。[BindProperty]
屬性可讓模型繫結。[BindProperty] public Movie Movie { get; set; }
如果模型狀態中有錯誤 (例如
ReleaseDate
無法轉換為日期),則會以提交的值重新顯示表單。如果沒有任何模型錯誤,則會儲存電影。
Index、Create 和 Delete Razor 頁面中的 HTTP GET 方法都會依循類似的模式。 Create Razor Page中的 HTTP POST OnPostAsync
方法,會依循與 Edit Razor 頁面中的 OnPostAsync
方法類似的模式。