Исключение дублирования ключа Postgres 23505 в EF Core AddOrUpdate (условие гонки при первоначальной вставке)C#

Место общения программистов C#
Ответить
Anonymous
 Исключение дублирования ключа Postgres 23505 в EF Core AddOrUpdate (условие гонки при первоначальной вставке)

Сообщение Anonymous »

Я обнаружил, что повторяющееся значение ключа нарушает уникальное ограничение в моем приложении ASP.NET Core, и хотя я понимаю, почему это происходит, я не уверен, как лучше всего справиться с этим с помощью EF Core и PostgreSQL (Npgsql).
У меня есть высокочастотная конечная точка, которая получает координаты GPS от мобильных приложений.

Код: Выделить всё

[HttpPost("{courierId:guid}/locations")]
public async Task ReceiveLocations(Guid courierId, [FromBody] LocationsDto locationsDto)
{
Я сохраняю последнее местоположение в таблице LastLocations. Первичным ключом является CourierId, поэтому существует строгая связь 1-к-1 (одна запись на каждого курьера). Логика проста: если запись существует, обновите ее; в противном случае вставьте его.
Вот мой метод репозитория:

Код: Выделить всё

public async Task AddOrUpdate(LastLocationEntity lastLocationEntity)
{
var existingCourierLocation = await applicationDbContext.LastLocations
.FindAsync(lastLocationEntity.CourierId);

if (existingCourierLocation is null)
applicationDbContext.LastLocations.Add(lastLocationEntity);
else
applicationDbContext.Entry(existingCourierLocation).CurrentValues.SetValues(lastLocationEntity);

await applicationDbContext.SaveChangesAsync();
}
Редко я вижу в журналах следующее исключение:

Код: Выделить всё

Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes.
---> Npgsql.PostgresException (0x80004005): 23505: duplicate key value violates unique constraint "PK_LastLocations"
...
Detail: Key ("CourierId")=(...) already exists.
Мой анализ

Я успешно воспроизвел это с помощью k6, отправив пакет одновременных запросов для одного и того же CourierId в пустую базу данных.
Я понимаю, что это Состояние гонки во время первоначальной вставки:
  • Два (или более) запроса выполняют FindAsync одновременно.
  • Оба получают null, поскольку транзакция еще не зафиксирована.
  • Оба пытаются добавить новый объект.
  • Код: Выделить всё

    SaveChangesAsync
    вызывается. Первый выполняется успешно, второй завершается с ошибкой 23505.
Если запись уже существует в базе данных, ошибка никогда не возникает, поскольку FindAsync правильно возвращает объект, и код переходит к пути обновления.
Каков стандартный способ обработки этого сценария «Вставка или обновление» (Upsert) в EF Core, чтобы избежать условий гонки?
/>Следует ли мне заключить это в блок try-catch с политикой повтора или есть способ выполнить атомарный UPSERT (например, INSERT ... ON CONFLICT) непосредственно в EF Core, не переходя к необработанному SQL?
Спасибо!>

Подробнее здесь: https://stackoverflow.com/questions/798 ... ndition-on
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»