学习ASP.NET Core Razor 编程系列十八——并发解决方案
?
學(xué)習(xí)ASP.NET Core Razor 編程系列目錄
學(xué)習(xí)ASP.NET Core Razor 編程系列一
學(xué)習(xí)ASP.NET Core Razor 編程系列二——添加一個實體
?學(xué)習(xí)ASP.NET Core Razor 編程系列三——創(chuàng)建數(shù)據(jù)表及創(chuàng)建項目基本頁面
學(xué)習(xí)ASP.NET Core Razor 編程系列四——Asp.Net Core Razor列表模板頁面
學(xué)習(xí)ASP.NET Core Razor 編程系列五——Asp.Net Core Razor新建模板頁面
學(xué)習(xí)ASP.NET Core Razor 編程系列六——數(shù)據(jù)庫初始化
學(xué)習(xí)ASP.NET Core Razor 編程系列七——修改列表頁面
學(xué)習(xí)ASP.NET Core Razor 編程系列八——并發(fā)處理
學(xué)習(xí)ASP.NET Core Razor 編程系列九——增加查詢功能
?學(xué)習(xí)ASP.NET Core Razor 編程系列十——添加新字段
學(xué)習(xí)ASP.NET Core Razor 編程系列十一——把新字段更新到數(shù)據(jù)庫
學(xué)習(xí)ASP.NET Core Razor 編程系列十二——在頁面中增加校驗
學(xué)習(xí)ASP.NET Core Razor 編程系列十三——文件上傳功能(一)
學(xué)習(xí)ASP.NET Core Razor 編程系列十四——文件上傳功能(二)
學(xué)習(xí)ASP.NET Core Razor 編程系列十五——文件上傳功能(三)
學(xué)習(xí)ASP.NET Core Razor 編程系列十六——排序
?學(xué)習(xí)ASP.NET Core Razor 編程系列十七——分組
?
?
???? 在文章(學(xué)習(xí)ASP.NET Core Razor 編程系列八——并發(fā)處理)中對于并發(fā)錯誤,我們只是簡單粗暴的進(jìn)行了異常捕獲,然后拋出了異常。在本文中我們來看兩個解決并發(fā)的方法。
??? 樂觀并發(fā)的解決方案有以下三種:
??? 1) 可以跟蹤用戶已修改的屬性,并僅更新數(shù)據(jù)庫中相應(yīng)的列。
??? 在這種情況下,數(shù)據(jù)不會丟失。 兩個用戶更新了不同的字段內(nèi)容(例如:書名與出版社)。下次有人瀏覽書籍信息時,將看到書名和出版社兩個人的更改。 這種更新方法可以減少導(dǎo)致數(shù)據(jù)丟失的沖突數(shù)。這種方法需要維持重要狀態(tài),以便跟蹤所有數(shù)據(jù)庫值與當(dāng)前值,增加了應(yīng)用復(fù)雜,可能會影響應(yīng)用性能。通常不適用于 Web 應(yīng)用。
??? 2) 可讓后提交的用戶更改覆蓋之前用戶提交的更改。
??? 這種方法稱為“客戶端優(yōu)先”或“最后一個優(yōu)先”方案。 (客戶端的所有值優(yōu)先于數(shù)據(jù)存儲的值。)如果不對并發(fā)處理進(jìn)行任何編碼,則自動執(zhí)行“客戶端優(yōu)先”。
??? 3) 可以阻止在數(shù)據(jù)庫中更新后一用戶提交的更改。
??? 這種方法,需要顯示錯誤信息,顯示當(dāng)前數(shù)據(jù)和數(shù)據(jù)庫中的數(shù)據(jù),允許用戶重新修改,并保存。這稱為“存儲優(yōu)先”方案。 (數(shù)據(jù)存儲值優(yōu)先于客戶端提交的值。)
一、客戶端優(yōu)化
??? 接下去我們來看看“客戶端優(yōu)先”方案。 此方法確保后一用戶的提交為準(zhǔn),覆蓋數(shù)據(jù)庫中的數(shù)據(jù)。
??? 樂觀并發(fā)允許發(fā)生并發(fā)沖突,并在并發(fā)沖突發(fā)生時作出正確反應(yīng)。 例如,管理員訪問用書籍信息編輯頁面,將“Publishing”字段值修改為“清華大學(xué)出版社”。
1.首先,我們使用Visual Studio 2017打開Books\Edit.cshmtl.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");}2.在Visual Studio 2017中按F5運行應(yīng)用程序。在瀏覽器中瀏覽書籍信息,并在書籍列表頁面中選擇一條書籍信息。我們假設(shè)有兩個用戶要對此條書籍信息進(jìn)行編輯。首先是管理員,對此條書籍信息修改了“Publishing”的信息。如下圖。
?
3.在管理員單擊“Save”按鈕之前,Test用戶訪問了相同頁面,并將“出版日期”修改為了“2018-01-08”。如下圖。
?
4.Test用戶先單擊“保存”,并在瀏覽器的書籍信息列表頁面中看到了他修改的出版日期數(shù)據(jù)保存到了數(shù)據(jù)庫。如下圖。
?
5.此時,管理員單擊“編輯”頁面上的“保存”,但頁面的上的“出版日期”還是“2018-01-13”,按照“客戶端優(yōu)化”規(guī)則會把Test用戶的修改覆蓋掉。如下圖。
?
?
?
二、存儲優(yōu)先
?
??? 接下去我們來看看“存儲優(yōu)先”方案。 此方法可確保用戶在未收到警報時不會覆蓋任何更改。
??? 首先我們來了解三組值:
- “當(dāng)前值”是應(yīng)用程序嘗試寫入數(shù)據(jù)庫的值。
- “原始值”是在進(jìn)行任何編輯之前最初從數(shù)據(jù)庫中檢索的值。
- “數(shù)據(jù)庫值”是當(dāng)前存儲在數(shù)據(jù)庫中的值。
??? 處理并發(fā)沖突的常規(guī)方法是:
???? 1)在 SaveChanges 期間捕獲 DbUpdateConcurrencyException。
??? 2)使用 DbUpdateConcurrencyException.Entries 為受影響的實體準(zhǔn)備一組新更改。
??? 3)刷新并發(fā)令牌的原始值以反映數(shù)據(jù)庫中的當(dāng)前值。
??? 4)重試該過程,直到不發(fā)生任何沖突。
?? 下面的示例,使用時間戳作為行級版本號。
1. 在Visual Studio 2017的“解決方案資源管理器”中使用鼠標(biāo)左鍵雙擊打開 Models /Book.cs文件, 對User實體添加跟蹤屬性RowVersion,并在其上添加Timestamp特性。代碼如下:
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; namespace RazorMvcBooks.Models {public class Book{ public int ID { get; set; }[Required][StringLength(50, MinimumLength = 2)]public string Name { get; set; }[Display(Name = "出版日期")][DataType(DataType.Date)]public DateTime ReleaseDate { get; set; }[Range(1,200)][DataType(DataType.Currency)]public decimal Price { get; set; }public string Author { get; set; }[ Required]public string Publishing { get; set; }1712995269public byte[] RowVersion { get; set; }}}?
2.在Visual Studio 2017中選擇“菜單>Nuget包管理器>程序包管理器控制臺”,然后在打開的程序包管理器控制臺依次執(zhí)行以下命令
Add-Migration RowVer Update-Database3.在SQL Server Management Studio中查看Book表。如下圖。
?
4.在Visual Studio 2017的“解決方案資源管理器”中使用鼠標(biāo)左鍵雙擊打開 Pages/Books/Edit.cshtml.cs文件,對OnPostAsync方法進(jìn)行修改。Entity Framework Core 使用包含原始 RowVersion 值的 WHERE 子句生成 SQL UPDATE 命令。如果沒有行受到 UPDATE 命令影響(沒有行具有原始 RowVersion 值),將引發(fā) DbUpdateConcurrencyException 異常。代碼如下:
public async Task<IActionResult> OnPostAsync(){if (!ModelState.IsValid){return Page();}var updBook = _context.Book.AsNoTracking().Where(u => u.ID == Book.ID).First();// 如果為null,則當(dāng)前用戶信息已經(jīng)被 刪除if (updBook == null){return HandDeleteBook();}_context.Attach(Book).State = EntityState.Modified;if (await TryUpdateModelAsync<Book>(Book,"Book",s => s.Name, s =>s.Publishing, s => s.ReleaseDate, s => s.Price)){try{await _context.SaveChangesAsync();return RedirectToPage("./Index");}catch (DbUpdateConcurrencyException ex){var exceptionEntry = ex.Entries.Single();var clientValues = (Book)exceptionEntry.Entity;var databaseEntry = exceptionEntry.GetDatabaseValues();if (databaseEntry == null){ModelState.AddModelError(string.Empty, "保存失敗!.當(dāng)前用戶信息已經(jīng)被刪除");return Page();}var dbValues = (Book)databaseEntry.ToObject();setDbErrorMessage(dbValues, clientValues, _context);//用數(shù)據(jù)庫中的 RowVersion 值設(shè)置為當(dāng)前實體對象客戶端界面中的RowVersion值。 用戶下次單擊“保存”時,將僅捕獲最后一次顯示編輯頁后發(fā)生的并發(fā)錯誤。 Book.RowVersion = (byte[])dbValues.RowVersion;//ModelState 具有舊的 RowVersion 值,因此需使用 ModelState.Remove 語句。 在 Razor 頁面中,//當(dāng)兩者都存在時,字段的 ModelState 值優(yōu)于模型屬性值。 ModelState.Remove("Book.RowVersion");}}return Page();}private PageResult HandDeleteBook(){Book deletedDepartment = new Book();ModelState.AddModelError(string.Empty,"保存失敗!.當(dāng)前書籍信息已經(jīng)被刪除!");return Page();}
?
6.在Edit.cshtml.cs文件,添加setDbErrorMessage方法。為每列添加自定義錯誤消息,當(dāng)這些列中的數(shù)據(jù)庫值與客戶端界面上的值不同時,給出相應(yīng)的錯誤信息。代碼如下:?????
7.在Visual Studio 2017的“解決方案資源管理器”中使用鼠標(biāo)左鍵雙擊打開 Pages/Books/Edit.cshtml文件,? <form method="post">標(biāo)簽下面添加添加隱藏的行版本。必須添加 RowVersion,以便回發(fā)綁定值。
??? <input type="hidden" asp-for="Book.RowVersion" />
8.在Visual Studio 2017中按F5運行應(yīng)用程序。使用兩個瀏覽器打開同一條書籍信息記錄進(jìn)行編輯,此時兩個瀏覽器顯示的書籍信息是一樣的。瀏覽器1中的書籍信息界面。在修改了“Publishing”的數(shù)據(jù)由“清華大學(xué)出版社”修改為“機械工業(yè)出版社”,然后點擊“Save”按鈕。如下圖。
?
9.在瀏覽器中單擊“保存”之后,瀏覽器會自動跳轉(zhuǎn)到書籍信息列表頁面中看到了所修改的“Publishing”數(shù)據(jù)保存到了數(shù)據(jù)庫。如下圖。
?
?
10.在第二個瀏覽器中,修改“出版日期”的值,由“2018-01-13”改為“2018-01-08”。如下圖。
?
11.然后使用單擊“ Save”按鈕。此時由于客戶端界面上的信息與數(shù)據(jù)庫中的值不一樣,所以會出現(xiàn)錯誤提示信息。如下圖。
?
? 12. 把“Publishing”修改為“機械工業(yè)出版社”,再次單擊“保存”,將第二個瀏覽器中輸入的值保存到數(shù)據(jù)庫。 瀏覽器自動跳轉(zhuǎn)到書籍信息列表,可以看到保存的值。如下圖。
?
13.當(dāng)然如果你不做任何修改,再次點擊保存,也會把當(dāng)前頁面上的數(shù)據(jù)保存到數(shù)據(jù)庫中。如下圖。
?
?
總結(jié)
以上是生活随笔為你收集整理的学习ASP.NET Core Razor 编程系列十八——并发解决方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最大堆
- 下一篇: iOS面试知识点梳理