在EF Core中处理DbUpdateConcurrencyException

时间:2019-07-23 16:10:50

标签: entity-framework .net-core ef-core-2.0 ef-core-2.1 ef-core-2.2

我正在使用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() ??

有什么方法可以选择随机记录而不将整个记录集加载到内存中吗?

0 个答案:

没有答案