如何防止多个用户使用LINQ同时更新记录

时间:2019-07-11 05:14:44

标签: c# sql postgresql linq asp.net-core

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

在这里,第一个用户可以查询数据,并且应该锁定它以防止第二个用户查询相同的数据。在第一个用户的过程完成后,第二个用户查询应自动运行。

  

更新

例如:对于id:1,数量为1000,第一个用户请求将数量减少100,第二个用户同时发送请求减少100,然后第一个用户的saveChanges()生效。最终减少数量应为1000-100-100 = 800。

因此,直到第一个用户操作完成时,第二个用户的查询都应该在Que中。

我使用Asp.Net Core 2.2 Code First,PostgreSQL,无存储过程。

如何在此处锁定行?

6 个答案:

答案 0 :(得分:1)

我建议您使用以下原始更新查询:

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

它可以防止代码中遇到的并发问题。

请注意,qty >= @ConfirmQty会阻止将数量设置为0以下。您可以检查受影响的行,如果为0,则可以说没有足够的项目来抽取库存。

答案 1 :(得分:0)

尝试在查询中使用必须是原子的事务(无法对受此操作影响的数据执行任何操作)。

例如,阅读this

此外,请阅读有关不同锁定级别的信息,以防止同时进行更新等。

答案 2 :(得分:0)

这类锁定方案被称为悲观锁定,比乐观锁定的替代方案更容易出现问题。乐观实际上根本不是一种锁定机制。它使具有相同记录的任何一个用户都可以尝试进行编辑。作为更新的一部分,ORM将所有先前的值传递回dB并形成查询,以便它将当前的每个表值与orm已知的每个先前的值进行比较(从提取记录时开始)。如果其他用户更改了任何值,则更新将失败并返回0条记录更新。此时,orm可能会向您提出异常,表明其他人已经编辑了相同的记录。您可以使用此异常并通知用户,也许可以让他们选择要做什么:

  • 用我的覆盖他们的
  • 保留他们的遗物
  • 与他们的人合并

我确定在对更改进行编码并将其提交到源代码管理时已经看到了,):

当然,您必须编写一个接口才能执行此操作,但这通常是正确的选择,因为只有用户才能知道数据应该是什么。如果您不提供合并,那么它可以是“保留我的/保留他们的”的简单对话框

您的建议是将事情排在队列中,然后仅顺序地进行编辑,这对我来说意义不大,因为第二次编辑是在编辑第一个用户已经编辑过的数据;如果您以编程方式解决并发问题,您怎么知道最终将处于有效状态?如果您只是要用person2覆盖person1的更改,则根本不需要任何并发控制

我已经谈到了乐观并发系统的工作原理,但是假设您使用的是EF Core,那么精美的手册还有很多话要说: https://docs.microsoft.com/en-us/ef/core/saving/concurrency

答案 3 :(得分:0)

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();
    }

答案 4 :(得分:0)

我要做的是在每个基本实体中添加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
 }

任何更新都会增加版本,并且不会与具有先前版本的其他用户匹配

答案 5 :(得分:0)

您可以在表的字段顶部添加 ConcurrencyCheck 属性以确保不会发生冲突。

this this