Мне нужно реализовать пессимистичный контроль параллелизма.
По сути, я хочу дождаться завершения действия, прежде чем разрешить его выполнение во второй раз, потому что я хочу, чтобы одновременно существовала только 1 строка с определенным значением.
Пример:
// I would like to block concurrent execution of this method (lock it until it's finished)
[HttpPost("open")]
public async Task<IActionResult> Open(ReportCashDrawerStateRequest request)
{
...
// CONCURRENCY PROBLEM HERE:
// This check for uniqueness will pass if Open will be executed concurrently
// before SaveChangesAsync is called which will result in duplicate rows
if (await _db.CashDrawerStates.AnyAsync(s => s.CashDrawerId == request.CashDrawerId && s.EndTime == null))
return UnprocessableEntity("Already open");
var cashDrawerState = new CashDrawerState
{
CashDrawerId = request.CashDrawerId,
StartTime = DateTime.UtcNow,
StartedById = User.GetUserId(),
StartingCashAmount = request.CashAmount
};
// because check above will pass this will result in having 2 rows with EndTime==null
// which is unwanted.
_db.CashDrawerStates.Add(cashDrawerState);
await _db.SaveChangesAsync();
...
}
Это требование бизнес-логики, я думаю, что добавление уникального ограничения (индекса) решит эту проблему.
Но есть ли способ решить эту проблему, реализуя какую-то блокировку в методе Open без добавления уникального ограничения на столбец базы данных?
Я читал https://docs.microsoft.com/en-us/ef/core/saving/concurrency, но он описывает только обработку конфликта для обновлений и удалений, а не для вставок.
@mjwills Нет, не видел. Вы хотите сказать, что это предотвратит конфликт? (заблокируйте его, пока он не закончится)
@Konrad Зачем, вы хотите использовать пессимистичный параллелизм? Это снижает производительность много. Именно поэтому он не используется с конца 90-х годов. Во всяком случае, проблема заключается в выполнении этой проверки уникальности. В SQL вы можете написать INSERT FROM WHERE и убедиться, что вы вставили запись только в том случае, если она еще не существует. Вы также можете вернуть любые вставленные значения с помощью предложения OUTPUT. Эта единственная операция будет атомарной
@Konrad, это один из сценариев, когда ORM не могу хорошо работать. Гораздо лучше использовать хранимую процедуру или запрос и, возможно, отображать результаты.
@PanagiotisKanavos Я не знал. Я открыт для лучших решений. Для меня важно сообщить пользователю, что он уже открыт.
@PanagiotisKanavos, как вы говорите, сработало бы, если бы это было в 1 отдельном запросе (INSERT INTO WHERE) или если бы у меня был установлен уникальный набор ограничений, тогда это был бы вопрос обработки исключения.
Связано: stackoverflow.com/questions/27005269/…





Вы пробовали использовать
TransactionScope? weblogs.thinktecture.com/pawel/2018/06/…