學習ASP.NET Core Razor 編程系列十八——併發解決方案

来源:https://www.cnblogs.com/chillsrc/archive/2018/09/21/9687312.html
-Advertisement-
Play Games

樂觀併發的解決方案有以下三種: 1) 可以跟蹤用戶已修改的屬性,並僅更新資料庫中相應的列。 2) 可讓後提交的用戶更改覆蓋之前用戶提交的更改。 3) 可以阻止在資料庫中更新後一用戶提交的更改。 ...


 

學習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 編程系列九——增加查詢功能

 學習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) 可以阻止在資料庫中更新後一用戶提交的更改。

    這種方法,需要顯示錯誤信息,顯示當前數據和資料庫中的數據,允許用戶重新修改,並保存。這稱為“存儲優先”方案。 (數據存儲值優先於客戶端提交的值。)

一、客戶端優化

    接下去我們來看看“客戶端優先”方案。 此方法確保後一用戶的提交為準,覆蓋資料庫中的數據。

    樂觀併發允許發生併發衝突,併在併發衝突發生時作出正確反應。 例如,管理員訪問用書籍信息編輯頁面,將“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用戶的修改覆蓋掉。如下圖。

 

 

 

二、存儲優先

 

    接下去我們來看看“存儲優先”方案。 此方法可確保用戶在未收到警報時不會覆蓋任何更改。

    首先我們來瞭解三組值:

  • “當前值”是應用程式嘗試寫入資料庫的值。
  • “原始值”是在進行任何編輯之前最初從資料庫中檢索的值。
  • “資料庫值”是當前存儲在資料庫中的值。

    處理併發衝突的常規方法是:

     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-Migration RowVer
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.當然如果你不做任何修改,再次點擊保存,也會把當前頁面上的數據保存到資料庫中。如下圖。

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.分析 上傳文件的過程:客服端選擇一個文件後,寫入到伺服器端,伺服器端使用一個目錄來存儲該文件--底層IO流操作 2.jsp文件上的表單設計 表單傳輸格式用multipart/form-data,要上傳的文件input標簽name屬性最好用同樣的首碼或者尾碼好獲取 3後臺Servlet處理 1.S ...
  • 題意 題目鏈接 題意:給出一張無向圖,每次詢問兩點之間的最短路,滿足$m - n <= 20$ $n, m, q \leqslant 10^5$ Sol 非常好的一道題。 首先建出一個dfs樹。 因為邊數-點數非常少,所以我們可以對於某些非樹邊特殊考慮。 具體做法是:對於非樹邊連接的兩個點,暴力求出 ...
  • 分散式架構有以下幾點普適性的共性需求: 1. 提供集群的集中化的配置管理功能,可以不重啟就讓新的配置參數生效,類似與配置中心 2. 簡單可靠的集群節點動態發現機制,便於動態發現服務,動態擴展節點 3. 簡單可靠的leader選舉機制 4. 提供分散式鎖 zookeeper的數據結構整體上可以看作一顆 ...
  • 在前面大致預覽了常用變數的結構之後,我們今天來仔細的剖析一下字元串的具體實現。 一、字元串的結構 zend_refcounted_h對應的結構體: 下麵我們來瞭解一下具體每個成員的作用: gc:就是_zend_refcounted_h結構體,主要作用是引用計數以及標記變數的類別。 h:字元串的哈希值 ...
  • 前言 並行編程:通過編碼方式利用多核或多處理器稱為並行編程,多線程概念的一個子集。 並行處理:把正在執行的大量的任務分割成小塊,分配給多個同時運行的線程。多線程的一種。 並行編程分為如下幾個結構: 1.並行的LINQ或PLINQ 2.Parallel類 3.任務並行結構 4.併發集合 5.SpinL ...
  • 前言 本章講述正確添加語言資源的方式,以及一段語言資源的多種樣式顯示。 例如:“@Winter,你好!感謝已使用軟體 800 天!” 在添加如上多語言資源項時,“XX,你好!感謝已使用軟體 X 天!” 那麼,你是怎麼添加語言資源的呢? 分別添加,“,你好!”、“感謝已使用軟體”、“年”3個,再通過界 ...
  • 錯誤 無法安裝程式包“MiniProfiler.EF6 4.0.138”。你正在嘗試將此程式包安裝到目標為“.NETFramework,Version=v4.5.2”的項目中, 但該程式包不包含任何與該框架相容的程式集引用或內容文件。有關詳細信息,請聯繫程式包作者。 產生這個錯誤的原因主要是.net ...
  • 情景:公司項目基於.net4.0,web客戶端實現單點登錄需要自己解密id_token,對於jwt解密,.net提供了IdentityModel類庫,但是4.0中該類庫不可用,所以自己實現瞭解密方法.. 使用了類庫:https://github.com/jwt-dotnet/jwt 下麵直接貼代碼, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...