ASP.NET Web API C # Параллельные запросы, вызывающие дубликаты в базе данных - PullRequest
0 голосов
/ 30 мая 2018

У меня есть метод асинхронного контроллера WebApi, который вызывает другой асинхронный метод, который сначала проверяет, не существует ли запись, и не добавляет ли она ее в базу данных.Проблема в том, что если я сказал, что 3 запроса приходят одновременно, все проверки на ноль происходят одновременно в разных потоках (я полагаю), и я получу 2 повторяющихся записи.Например:

 public async void DoSomething()
{
    var record = {query that returns record or null}
    if (record == null)
    {
        AddNewRecordToDatabase();
    }   
}

... Это кажется очень распространенным явлением, и, может быть, я что-то упускаю, но как мне предотвратить это?Конечно, я должен попытаться заставить его создавать дубликаты, но это требование не разрешать делать это.

Заранее спасибо,

Ли

Ответы [ 3 ]

0 голосов
/ 30 мая 2018

Я бы решил это, поместив уникальные ограничения в слой данных.Предполагая, что вашим источником данных является sql, вы можете наложить уникальные ограничения на столбцы, к которым вы запрашиваете, с помощью «запроса, который возвращает запись или ноль», и это предотвратит эти дубликаты.Проблема с использованием блокировки или мьютекса в том, что она не масштабируется на несколько экземпляров службы.Вы должны иметь возможность развертывать множество экземпляров своей службы (на разных машинах), иметь любой из этих экземпляров для обработки запросов и при этом иметь согласованное поведение.В этой ситуации мьютекс или блокировка не защитят вас от этой проблемы параллелизма.

0 голосов
/ 31 мая 2018

Есть несколько ответов на это, в зависимости от деталей и того, с чем ваша команда чувствует себя комфортно.

  • Лучший и самый эффективный ответ на него, чтобы изменить ваш код C # так, чтобы вместовызывая операцию базы данных CRUD, она вызывает хранимую процедуру, которую вы пишете.Хранимая процедура будет проверять наличие и вставлять или обновлять только при необходимости.Специфика полностью под вашим контролем, так как вы пишете код.

  • Если вы хотите придерживаться обычных операций CRUD, вы можете заставить базу данных сериализовать запросы один за другим, заключив их в транзакцию и используя строгий уровень изоляции транзакции.На SQL Server вы хотите использовать сериализуемый.Это предотвратит любую транзакцию от изменения состояния таблицы за короткое время между частью, где вы проверяете существование, и когда вы вставляете запись.В этой статье приведен список уровней изоляции транзакций и способы их применения в коде c #.Если вы сделаете это, существует риск тупика, поэтому вам нужно будет catch и проглотить эти конкретные ошибки.

  • Если вам это нужно только для обеспечения уникальности, и новыйзапись имеет естественный (не суррогатный) ключ , вы можете добавить ограничение уникальности для ключа, что будет препятствовать успешному выполнению второй вставки.Это решение не очень хорошо работает с суррогатными ключами;на самом деле это не решает проблему, а просто перемещает ее в процесс генерации суррогатного ключа.Но если у вас есть приличный натуральный ключ, это очень легко реализовать.

0 голосов
/ 30 мая 2018

Я предотвращаю это при асинхронных вызовах, вместо этого вызывая хранимую процедуру.Затем хранимая процедура выполняет проверку с помощью «Обнаружения дублирующегося ключа» или аналогичного запроса для базы данных MSSQL.

Таким образом, это просто порядок асинхронных вызовов, который определяет, что является созданием,а что нет.

...