我有下面的LINQ更新记录,在某些情况下,它可能是IEnumerable
太。
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,同时第二个用户在第一个用户的SaveChanges()
生效之前发出减少数量100的请求,最终减少数量应为1000 - 100 - 100 = 800。
因此,在第一个用户的操作完成之前,第二个用户的查询应该在队列中。
我使用ASP.NET核心2.2代码优先,PostgreSQL,没有存储过程。
如何在此处锁定行?
6条答案
按热度按时间ej83mcc01#
尝试在查询中使用必须是原子的事务(不能对受此操作影响的数据执行任何操作)。
例如,读取this。
此外,请阅读有关不同锁定级别的信息,以防止同时更新等。
8hhllhi22#
这样的锁定机制被称为悲观锁定,并且比乐观锁定更容易出现问题。作为更新的一部分,ORM将所有先前的值传递回DB,并形成查询,以便将每个当前表值与ORM已知的每个先前值进行比较(从提取记录的时间开始)。如果其他用户更改了任何值,则更新失败并返回0个记录更新。此时,表单可以向您引发一个异常,指示其他人编辑了同一条记录。您可以使用此异常并通知用户,也许可以让他们选择要做什么:
我敢肯定,您在编写代码并将更改提交到源代码管理时已经看到了这一点:)
当然,你必须写一个接口来完成这个操作,但这通常是正确的做法,因为只有用户才能知道数据应该是什么。如果你不提供合并,那么它可以是一个简单的对话框“保留我的/保留他们的”
您建议将内容保持在队列中并只按顺序应用编辑,这对我来说没有多大意义,因为第二次编辑是编辑第一个用户已经编辑过的数据;如果以编程的方式解决并发问题,您怎么知道最终会得到一个有效的状态呢?如果您只是打算用person 2的更改覆盖person 1的更改,那么您根本不需要任何并发控制
我已经介绍了乐观并发系统的工作原理,但是假设您使用的是EF Core,那么这本优秀的手册还有很多要说的:https://learn.microsoft.com/en-us/ef/core/saving/concurrency
jogvjijk3#
我建议您使用以下原始更新查询:
它可以防止您在代码中遇到并发问题。
请注意
qty >= @ConfirmQty
,它可防止将数量设置为低于0。您可以检查受影响的行,如果数量为0,则可以说没有足够的项目来提取库存。pieyvz9o4#
lxkprmvk5#
我所做的是在每个基本实体中添加int Version,以检查其版本是否与每个数据的当前版本匹配。
且每个SaveChanges()都将具有以下属性
那么如果我们两个都将具有Version = 1,并且我们中的一个在另一个之前更新了相同的字段,则将由于Version增量而导致错误。
样品检查如下
任何更新都将增加版本,并且不会与具有先前版本的其他用户匹配
b5lpy0ml6#
您可以在表的字段顶部设置
ConcurrencyCheck
属性,以确保不会发生冲突。请参阅this和this