學習ASP.NET Core Razor 編程系列目錄html
學習ASP.NET Core Razor 編程系列二——添加一個實體編程
學習ASP.NET Core Razor 編程系列三——建立數據表及建立項目基本頁面瀏覽器
學習ASP.NET Core Razor 編程系列四——Asp.Net Core Razor列表模板頁面併發
學習ASP.NET Core Razor 編程系列五——Asp.Net Core Razor新建模板頁面async
學習ASP.NET Core Razor 編程系列六——數據庫初始化post
學習ASP.NET Core Razor 編程系列七——修改列表頁面性能
學習ASP.NET Core Razor 編程系列八——併發處理學習
學習ASP.NET Core Razor 編程系列九——增長查詢功能優化
學習ASP.NET Core Razor 編程系列十——添加新字段
學習ASP.NET Core Razor 編程系列十一——把新字段更新到數據庫
學習ASP.NET Core Razor 編程系列十二——在頁面中增長校驗
學習ASP.NET Core Razor 編程系列十三——文件上傳功能(一)
學習ASP.NET Core Razor 編程系列十四——文件上傳功能(二)
學習ASP.NET Core Razor 編程系列十五——文件上傳功能(三)
學習ASP.NET Core Razor 編程系列十六——排序
學習ASP.NET Core Razor 編程系列十七——分組
在文章(學習ASP.NET Core Razor 編程系列八——併發處理)中對於併發錯誤,咱們只是簡單粗暴的進行了異常捕獲,而後拋出了異常。在本文中咱們來看兩個解決併發的方法。
樂觀併發的解決方案有如下三種:
1) 能夠跟蹤用戶已修改的屬性,並僅更新數據庫中相應的列。
在這種狀況下,數據不會丟失。 兩個用戶更新了不一樣的字段內容(例如:書名與出版社)。下次有人瀏覽書籍信息時,將看到書名和出版社兩我的的更改。 這種更新方法能夠減小致使數據丟失的衝突數。這種方法須要維持重要狀態,以便跟蹤全部數據庫值與當前值,增長了應用複雜,可能會影響應用性能。一般不適用於 Web 應用。
2) 可以讓後提交的用戶更改覆蓋以前用戶提交的更改。
這種方法稱爲「客戶端優先」或「最後一個優先」方案。 (客戶端的全部值優先於數據存儲的值。)若是不對併發處理進行任何編碼,則自動執行「客戶端優先」。
3) 能夠阻止在數據庫中更新後一用戶提交的更改。
這種方法,須要顯示錯誤信息,顯示當前數據和數據庫中的數據,容許用戶從新修改,並保存。這稱爲「存儲優先」方案。 (數據存儲值優先於客戶端提交的值。)
1、客戶端優先
接下去咱們來看看「客戶端優先」方案。 此方法確保後一用戶的提交爲準,覆蓋數據庫中的數據。
樂觀併發容許發生併發衝突,並在併發衝突發生時做出正確反應。 例如,管理員訪問用書籍信息編輯頁面,將「Publishing」字段值修改成「清華大學出版社」。
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運行應用程序。在瀏覽器中瀏覽書籍信息,並在書籍列表頁面中選擇一條書籍信息。咱們假設有兩個用戶要對此條書籍信息進行編輯。首先是管理員,對此條書籍信息修改了「Publishing」的信息。以下圖。
3.在管理員單擊「Save」按鈕以前,Test用戶訪問了相同頁面,並將「出版日期」修改成了「2018-01-08」。以下圖。
4.Test用戶先單擊「保存」,並在瀏覽器的書籍信息列表頁面中看到了他修改的出版日期數據保存到了數據庫。以下圖。
5.此時,管理員單擊「編輯」頁面上的「保存」,但頁面的上的「出版日期」仍是「2018-01-13」,按照「客戶端優化」規則會把Test用戶的修改覆蓋掉。以下圖。
2、存儲優先
接下去咱們來看看「存儲優先」方案。 此方法可確保用戶在未收到警報時不會覆蓋任何更改。
首先咱們來了解三組值:
處理併發衝突的常規方法是:
1)在 SaveChanges
期間捕獲 DbUpdateConcurrencyException
。
2)使用 DbUpdateConcurrencyException.Entries
爲受影響的實體準備一組新更改。
3)刷新併發令牌的原始值以反映數據庫中的當前值。
4)重試該過程,直到不發生任何衝突。
下面的示例,使用時間戳做爲行級版本號。
1. 在Visual Studio 2017的「解決方案資源管理器」中使用鼠標左鍵雙擊打開 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; } [Timestamp] public byte[] RowVersion { get; set; } } }
2.在Visual Studio 2017中選擇「菜單>Nuget包管理器>程序包管理器控制檯」,而後在打開的程序包管理器控制檯依次執行如下命令
Add-MigrationRowVer
Update-Database
3.在SQL Server Management Studio中查看Book表。以下圖。
4.在Visual Studio 2017的「解決方案資源管理器」中使用鼠標左鍵雙擊打開 Pages/Books/Edit.cshtml.cs文件,對OnPostAsync方法進行修改。Entity Framework Core 使用包含原始 RowVersion 值的 WHERE 子句生成 SQL UPDATE 命令。若是沒有行受到 UPDATE 命令影響(沒有行具備原始 RowVersion 值),將引起 DbUpdateConcurrencyException 異常。代碼以下:
public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } var updBook = _context.Book.AsNoTracking().Where(u => u.ID == Book.ID).First(); // 若是爲null,則當前用戶信息已經被 刪除 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, "保存失敗!.當前用戶信息已經被刪除"); return Page(); } var dbValues = (Book)databaseEntry.ToObject(); setDbErrorMessage(dbValues, clientValues, _context); //用數據庫中的 RowVersion 值設置爲當前實體對象客戶端界面中的RowVersion值。 用戶下次單擊「保存」時,將僅捕獲最後一次顯示編輯頁後發生的併發錯誤。 Book.RowVersion = (byte[])dbValues.RowVersion; //ModelState 具備舊的 RowVersion 值,所以需使用 ModelState.Remove 語句。 在 Razor 頁面中,
//當二者都存在時,字段的 ModelState 值優於模型屬性值。 ModelState.Remove("Book.RowVersion"); } } return Page(); } private PageResult HandDeleteBook() { Book deletedDepartment = new Book(); ModelState.AddModelError(string.Empty, "保存失敗!.當前書籍信息已經被刪除!"); return Page(); }
6.在Edit.cshtml.cs文件,添加setDbErrorMessage方法。爲每列添加自定義錯誤消息,當這些列中的數據庫值與客戶端界面上的值不一樣時,給出相應的錯誤信息。代碼以下:
private void setDbErrorMessage(Book dbValues, Book clientValues, BookContext context) { if (dbValues.Name != clientValues.Name) { ModelState.AddModelError("Book.Name", $"數據庫值: {dbValues.Name}"); } if (dbValues.Publishing != clientValues.Publishing) { ModelState.AddModelError("Book.Publishing", $"數據庫值: {dbValues.Publishing}"); } if (dbValues.ReleaseDate != clientValues.ReleaseDate) { ModelState.AddModelError("Book.ReleaseDate", $"數據庫值: {dbValues.ReleaseDate}"); } if (dbValues.Price != clientValues.Price) { ModelState.AddModelError("Book.Price", $"數據庫值: {dbValues.Price}"); } ModelState.AddModelError(string.Empty,"您嘗試編輯的書籍信息記錄被另外一個用戶修改了。編輯操做被取消," + "數據庫中的當前值已經顯示。若是仍想編輯此記錄,請單擊「保存」按鈕。"); }
7.在Visual Studio 2017的「解決方案資源管理器」中使用鼠標左鍵雙擊打開 Pages/Books/Edit.cshtml文件, <form method="post">標籤下面添加添加隱藏的行版本。必須添加 RowVersion,以便回發綁定值。
<input type="hidden" asp-for="Book.RowVersion" />
8.在Visual Studio 2017中按F5運行應用程序。使用兩個瀏覽器打開同一條書籍信息記錄進行編輯,此時兩個瀏覽器顯示的書籍信息是同樣的。瀏覽器1中的書籍信息界面。在修改了「Publishing」的數據由「清華大學出版社」修改成「機械工業出版社」,而後點擊「Save」按鈕。以下圖。
9.在瀏覽器中單擊「保存」以後,瀏覽器會自動跳轉到書籍信息列表頁面中看到了所修改的「Publishing」數據保存到了數據庫。以下圖。
10.在第二個瀏覽器中,修改「出版日期」的值,由「2018-01-13」改成「2018-01-08」。以下圖。
11.而後使用單擊「 Save」按鈕。此時因爲客戶端界面上的信息與數據庫中的值不同,因此會出現錯誤提示信息。以下圖。
12. 把「Publishing」修改成「機械工業出版社」,再次單擊「保存」,將第二個瀏覽器中輸入的值保存到數據庫。 瀏覽器自動跳轉到書籍信息列表,能夠看到保存的值。以下圖。
13.固然若是你不作任何修改,再次點擊保存,也會把當前頁面上的數據保存到數據庫中。以下圖。