并发异常处理
在Visual Studio 2017的解决方案资源管理器中找到 Pages/Books/Edit.cshtml.cs 文件,鼠标双击打开 ,在代码中找到OnPostAsync方法。并按如下代码进行修改:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Book).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!_context.Book.Any(e => e.ID == Book.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
上面的代码功能是当检测到第一个客户端在删除书籍信息时,第二个客户端对要删除的书籍信息进行修改并保存时发生异常。
我们可以进行以下操作来重现上面的异常。
- 在 catch (DbUpdateConcurrencyException) 上设置断点。如下图。
2. 在Visual Studio 2017中按F5,运行应用程序,在打开的浏览器的一个窗口中,选择一本书籍进行修改。如下图。
3. 在另一个浏览器窗口中,选择同一本书籍信息的“Delete”链接,然后删除此书籍。
4. 在编辑书籍信息的浏览器窗口中,将书籍信息的修改内容保存到数据库。如下图。
5. 当两个或更多客户端同时更新记录时,代码通常将检测到并发冲突。如下图。
GET请求与POST请求
接下来我们根据 Pages/Books/Edit.cshtml.cs 文件内容来介绍一下请求过程,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using RazorMvcBooks.Models;
namespace RazorMvcBooks.Pages.Books
{
public class EditModel : PageModel
{
private readonly RazorMvcBooks.Models.BookContext _context;
public EditModel(RazorMvcBooks.Models.BookContext context)
{
_context = context;
}
[BindProperty]
public Book Book { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Book = await _context.Book.SingleOrDefaultAsync(m => m.ID == id);
if (Book == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Book).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!_context.Book.Any(e => e.ID == Book.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
}
}
1. 当浏览器对Books/Edit 页面发出 HTTP GET 请求时(例如 http://localhost:5000/Books/Edit/9):
- OnGetAsync 方法从数据库提取书籍信息并把数据传递给Page 方法。
- Page 方法呈现“Pages/Books/Edit.cshtml”Razor 页面。 Pages/Books/Edit.cshtml 文件包含实体指令 (@model RazorMvcBooks.Pages.Books.EditModel),这使书籍实体在页面上可用。
- 页面中的表单会显示书籍实体中的值。
2. 当浏览器对Books/Edit 页面发出Post请求时:
- 此页面上的表单值将绑定到 Book 属性上。 [BindProperty] 特性会启用实体属性绑定。具体代码参见上面的代码。
- 如果实体对象的属性值中存在错误(例如,ReleaseDate 无法被转换为日期),则会使用已提交的值再次请求表单。
- 如果实体对象的属性值中没有错误,则把书籍信息保存到数据库。
“Index.cshtml”、“Create.cshtml”和“delete.cshtml”Razor 页面中的 HTTP GET 方法的实现原理与上面所述的Get请求类似。 “Create.cshtml”Razor 页面中的 POST请求方法的实现原理与上面所述的POST请求类似。