并发异常处理

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



     上面的代码功能是当检测到第一个客户端在删除书籍信息时,第二个客户端对要删除的书籍信息进行修改并保存时发生异常。

      我们可以进行以下操作来重现上面的异常。

  1. 在 catch (DbUpdateConcurrencyException) 上设置断点。如下图。

 

netcore 大并发架构 .net 并发处理_测试

         2. 在Visual Studio 2017中按F5,运行应用程序,在打开的浏览器的一个窗口中,选择一本书籍进行修改。如下图。


netcore 大并发架构 .net 并发处理_测试_02

       3. 在另一个浏览器窗口中,选择同一本书籍信息的“Delete”链接,然后删除此书籍。 

netcore 大并发架构 .net 并发处理_数据库_03

             4. 在编辑书籍信息的浏览器窗口中,将书籍信息的修改内容保存到数据库。如下图。 

netcore 大并发架构 .net 并发处理_ASP_04

          5. 当两个或更多客户端同时更新记录时,代码通常将检测到并发冲突。如下图。

 

netcore 大并发架构 .net 并发处理_测试_05

 

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请求类似。