[英]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
}
任何更新都会增加版本,并且不会与具有先前版本的其他用户匹配
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.