我在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,无存储过程。
如何在此处锁定行?
答案 0 :(得分:1)
我建议您使用以下原始更新查询:
UPDATE MyInv
SET qty = qty - @confirmQty
WHERE ID = @ID AND qty >= @ConfirmQty
它可以防止代码中遇到的并发问题。
请注意,qty >= @ConfirmQty
会阻止将数量设置为0以下。您可以检查受影响的行,如果为0,则可以说没有足够的项目来抽取库存。
答案 1 :(得分:0)
答案 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)