我正在使用EF Core 2.2.4。所有SQL表都有timestamp
列。我下面的方法试图获取给定用户的下一个WorkOrder。如果有多个用户同时竞争,我们可能会收到DbUpdateConcurrencyException,我可以使用重试逻辑来处理。
请注意,DBCntext作为Scoped
实例被注入到服务的构造函数中
public async Task<WorkOrder> GetNextOrder(int userID)
{
var maxTry = 3;
WorkOrder order = null;
for (int i = 0; i < maxTry; i++)
{
order = await _dbContext.WorkOrder
.Where(o => o.UserID == null)
.OrderBy(o => o.CreatedDateTime)
.FirstOrDefaultAsync();
if (order == null)
{
break; //exit out of the loop
}
order.StatusID = (int)Statuses.InProgress;
order.UserID = userID;
order.AssignedDateTime = DateTime.UtcNow;
try
{
await _dbContext.SaveChangesAsync();
i = maxTry;
}
catch (DbUpdateConcurrencyException ex)
{
// since mutiple users are compting for orders. We may get concurrency issue
// in such case retry to get next order
await _dbContext.Entry(order).ReloadAsync();
order = null;
}
}
return order;
}
这一直在起作用,但是现在用户数量有所增加,并且当他们争夺WorkOrder时,大部分时间以上的方法都会耗费大量精力,并为WorkOrder返回null。当然,我可以将计数器增加到10或20,这可能会起作用,但是随着用户的增加,我无法继续增加计数器。
我想知道是否有一种方法可以锁定选定的记录,以便其他执行不读取相同的记录或等到第一个记录完成? 我绝对不会锁定整个表
我尝试了交易,但没有成功
public async Task<WorkOrder> GetNextOrder(int userID)
{
var maxTry = 3;
WorkOrder order = null;
using (var transaction = await _dbContext.Database.BeginTransactionAsync())
{
for (int i = 0; i < maxTry; i++)
{
order = await _dbContext.WorkOrder
.Where(o => o.UserID == null)
.OrderBy(o => o.CreatedDateTime)
.FirstOrDefaultAsync();
if (order == null)
{
break; //exit out of the loop
}
order.StatusID = (int)Statuses.InProgress;
order.UserID = userID;
order.AssignedDateTime = DateTime.UtcNow;
try
{
await _dbContext.SaveChangesAsync();
i = maxTry;
}
catch (DbUpdateConcurrencyException ex)
{
// since mutiple users are compting for orders. We may get concurrency issue
// in such case retry to get next order
await _dbContext.Entry(order).ReloadAsync();
order = null;
}
}
transaction.Commit();
}
return order;
}
更新1
因此,EF Core不支持开箱即用的https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html,它仅支持开放式并发。这意味着您允许并发冲突发生,然后做出相应的反应。
因此,我在考虑的下一个解决方案不是采用FirstOrDefault()
,而是可以采用随机记录吗?
order = await _dbContext.WorkOrder
.Where(o => o.UserID == null)
.OrderBy(o => o.CreatedDateTime)
.TakeRandonRecord() ??
有什么方法可以选择随机记录而不将整个记录集加载到内存中吗?