【问题标题】:Duplicate key value violates unique constraint on SaveChangesAsync重复键值违反 SaveChangesAsync 上的唯一约束
【发布时间】:2019-10-21 14:54:21
【问题描述】:

我在控制器中有以下代码

public async Task<IActionResult> Post(string imei)
{
    var device = await _dbContext.Devices.FirstOrDefaultAsync(x => x.Imei == imei);
    if (device == null)
    {
        await _dbContext.Devices.AddAsync(new Device(imei));
        await _dbContext.SaveChangesAsync();
    }
    else
    {
        throw new ConflictException($"Device with IMEI '{imei}' already exists");
    }

    return Ok();
}

很难重现,但有时我得到了

23505:重复键值违反唯一约束 \"devices_imei_key\"

似乎device 为空,但当SaveChanges 时它已经不为空。我怎样才能避免使用try...catch?我可以使用AsNoTacking() 来提高数据库的读取效率吗?

【问题讨论】:

  • 这可能是time-of-check to time-of-use 问题 - 当您检查它不存在时,但当您保存它时,它已经创建了。您的应用添加了多少台设备?
  • 是的,似乎是。大约每分钟 10 个,但有时每秒 10 个
  • 如果这是您的应用程序设置的行为方式,即您可以在检查它和保存它之间设置一个设备 - 我想不出更简单的方法来处理它不是try catch,重复键的特定例外。另一种选择 - 创建一个用于插入新设备的存储过程并在其中处理这种情况 - 但在某种程度上你将不得不处理异常。
  • 您能否详细解释一下“在您检查它和保存它之间设置一个设备”。谢谢
  • @Chebur - 我的意思是在您检查它和尝试保存它之间正在设置一个新设备 - 这就是您在上面描述的场景。

标签: c# entity-framework-core asp.net-core-webapi


【解决方案1】:

这是基本的并发,与往常一样,您需要有一个特定的策略来处理并发。您当前使用的方法是不够的,除非您使用信号量对逻辑进行门控,这样一次只能有一个请求通过管道。这避开了并发问题,但显然会对性能产生影响,因为您在应用程序中创建了一个阻塞点。

由于您有唯一约束,因此您最好的选择是将代码包装在 try/catch 中并实际处理您所面临的情况。由于每个 IMEI 应该只有一个条目,因此您可以简单地执行与在 else 块中为 catch 块所做的相同的操作:返回 ConflictException

对于这个特定问题,除了添加锁或处理异常(使用 try/catch)之外没有其他选择。不管你跑得有多“快”,总是有可能发生并发冲突,你需要处理它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-13
    • 2020-03-27
    • 2013-06-23
    • 2016-06-12
    • 2013-03-15
    • 2016-11-27
    • 2018-03-14
    • 2016-09-27
    相关资源
    最近更新 更多