簡體   English   中英

如何使用LINQ防止多個用戶同時更新一條記錄

[英]How to prevent multiple users to update a record at the same time using LINQ

我在 LINQ 下更新記錄,在某些情況下它也可能是IEnumarable

    public async Task<IActionResult> MyMethod(Int ID, decimal confirmQty)
            {               
               using (var tran = _context.Database.BeginTransaction())
               {
                try
                {
                   // Need to Lock here whether single record or multiple records
                    var invReduce = _context.Inventorys.Where(w=>w.id == ID).FirstOrDefault();
                    invReduce.availQty -= confirmQty;

                    Thread.Sleep(60000); // One Min
                    await _context.SaveChangesAsync();
                    tran.Commit();
                    // Need to Un-Lock here whether single record or multiple records
                }
                catch (Exception ex)
                {
                    tran.Rollback();
                }
              }
             return Ok();
            }

在這里,第一個用戶可以查詢數據並且應該鎖定它以防止第二個用戶查詢相同的數據。 在第 1 個用戶的進程完成后,第 2 個用戶查詢應自動運行。

更新

例如:對於 id:1,數量為 1000,第一個用戶請求將數量減少 100,第二個用戶同時在第一個用戶的 saveChanges() 生效之前發送減少 100 的請求。 最終減少數量應為 1000 - 100 - 100 = 800。

因此,直到第 1 個用戶的操作完成時,第 2 個用戶的查詢應該在 Que 中。

我使用 Asp.Net Core 2.2 Code First,PostgreSQL,無存儲過程。

如何在此處鎖定行?

我建議您使用以下原始更新查詢:

UPDATE MyInv
SET qty = qty - @confirmQty
WHERE ID = @ID AND qty >=  @ConfirmQty

它可以防止代碼中遇到的並發問題。

注意qty >= @ConfirmQty它防止將數量設置為0以下。您可以檢查受影響的行,如果為0,則可以說沒有足夠的項目來拉出庫存。

嘗試在查詢中使用必須是原子的事務(無法對受此操作影響的數據執行任何操作)。

例如閱讀

此外,請閱讀有關不同鎖定級別的信息,以防止同時進行更新等。

這種鎖定方案被稱為悲觀鎖定,比樂觀鎖定的替代方案更容易出現問題。 樂觀實際上根本不是一種鎖定機制。 它使具有相同記錄的任何一個用戶都可以嘗試進行編輯。 作為更新的一部分,ORM將所有先前的值傳遞回dB並形成查詢,以便它將當前的每個表值與orm已知的每個先前的值進行比較(從提取記錄時開始)。 如果其他用戶更改了任何值,則更新將失敗並返回0條記錄更新。 此時,orm可能會向您提出異常,表明其他人已經編輯了相同的記錄。 您可以使用此異常並通知用戶,也許可以讓他們選擇要做什么:

  • 用我的覆蓋他們的
  • 保留自己的並放棄我的
  • 與他們的合並

您可以在對更改進行編碼並將其提交到源代碼管理時看到它,我相信:)

當然,您必須編寫一個接口才能執行此操作,但這通常是正確的做法,因為只有用戶才能知道數據應該是什么。 如果您不提供合並,則可以是“保留我的/保留他們的”的簡單對話框

您提出的將事情排在隊列中並僅順序應用編輯的建議對我來說意義不大,因為第二個編輯正在編輯第一個用戶已經編輯過的數據。 如果您以編程方式解決並發問題,您怎么知道最終將處於有效狀態? 如果您只是要用person2覆蓋person1的更改,則根本不需要任何並發控制

我已經談到了樂觀並發系統的工作原理,但是假設您使用的是EF Core,那么精美的手冊還有很多要說的: https : //docs.microsoft.com/zh-cn/ef/core/saving /並發

public async Task<IActionResult> MyMethod(Int ID, decimal confirmQty)
    {
        using(var tran = _context.Database.BeginTransaction())
        {
            try
            {

                var invReduce= _context.MyInv.Where(w => w.ID == ID).FirstOrDefault();
                inventoryReduce.qty -= confirmQty;
    // some long operation too goes here...
                await _context.SaveChangesAsync();

                tran.Commit();
            }
            catch
            {
                tran.Rollback();
                // log error if necessary
            }

            return Ok();
    }

我要做的是在每個基本實體中添加int版本,以檢查其版本是否與每個數據的當前版本匹配。

class Inventory
{
      //properties
      public int Version {get; set;}
}

並且每個SaveChanges()都將具有這些屬性

inventory.Version = inventory.Version + 1;

那么如果我們兩個人的Version = 1,並且我們一個人在另一個字段之前更新了相同字段,則會由於Version增量而導致錯誤。

下面的樣品檢查

var inventory = context.Inventory.FirstOrDefault(x => x.Id == Model.InventoryId);

 //check if inventory is not null if not continue
 if (inventory.Version != Model.Version)
 {
      //throw exception, DbContextConcurrencyIssue, error handling codes
 }
 else
 {
     //continue with process
 }

任何更新都會增加版本,並且不會與具有先前版本的其他用戶匹配

您可以在表的字段頂部設置ConcurrencyCheck屬性以確保不會發生沖突。

看到這個這個

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM