C# одновременные вызовы для sql db

В моем C# проекте у меня есть метод, который спрашивает, существует ли объект в БД, и если нет, то создает его. Теперь, если два пользователя задают один и тот же вопрос одновременно, они оба получают нулевое значение, поэтому поток будет сохранять в db, что невозможно для двух дубликатов, поэтому возникнет исключение sql. Как я могу решить эту проблему, пожалуйста?

вот мой код:

var date = DateTime.UtcNow.Date;


var todayCelebPageView = _celebPageViewsRepo.GetAll().SingleOrDefault(d => d.iCelebId == celebId && d.dDate == date);

if (todayCelebPageView != null)
{
    todayCelebPageView.iScore++;

    _celebPageViewsRepo.Save();
}
else
{
    todayCelebPageView = new MovliCelebPageView() {dDate = date, iCelebId = celebId, iScore = 1};
    _celebPageViewsRepo.Add(todayCelebPageView);
    _movliRepository.DbContext.Entry(todayCelebPageView).State = System.Data.EntityState.Added;
    _celebPageViewsRepo.Save();
}

3 ответа

На самом деле нет простого ответа на этот вопрос, это общая проблема с рядом решений.

Некоторые варианты могут быть:

  1. Перехватите правильное исключение SQL и повторите попытку соответственно
  2. Создайте очередь для этих вызовов базы данных и обрабатывайте их по одному
  3. Некоторая реализация блокировок, либо в базе данных (возможно, путем ее оборачивания в транзакции), либо в самом коде.

Что еще нужно учитывать, так это то, что должно произойти с точки зрения бизнеса, когда предпринимаются две попытки создать запись одновременно.

Должен ли человек, который создал запись, последний раз победить? Должен ли первый человек победить, а второй получить ошибку? Или вы должны написать первую запись и обновить ее со второй?

Ответ на этот вопрос будет полностью зависеть от специфики того, что вы пытаетесь сделать в своем приложении.

Переместите логику проверки и создайте на уровень процедуры, тогда она будет обработана с изоляцией транзакции:

IF NOT EXISTS (SELECT 'non-empty' FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.TABLE_NAME') AND type in (N'U'))
    CREATE TABLE dbo.TABLE_NAME

Но вы все равно должны обернуть свой метод и обработать исключение в соответствии с Number собственностью SqlException:

using (SqlConnection connection = new SqlConnection(connectionString))
{
    SqlCommand command = new SqlCommand(queryString, connection);
    try
    {
        command.Connection.Open();
        command.ExecuteNonQuery();
    }
    catch (SqlException ex)
    {
        for (int i = 0; i < ex.Errors.Count; i++)
        {
            errorMessages.Append("Index #" + i + "\n" +
                "Message: " + ex.Errors[i].Message + "\n" +
                "LineNumber: " + ex.Errors[i].LineNumber + "\n" +
                "Source: " + ex.Errors[i].Source + "\n" +
                "Procedure: " + ex.Errors[i].Procedure + "\n");
        }
        Console.WriteLine(errorMessages.ToString());
    }
}

Системные сообщения об ошибках
Причина и разрешение ошибок ядра СУБД

Вы должны обернуть тест на существование и вставить в транзакцию. Таким образом, второй вызов для проверки существования будет заблокирован, пока первый завершается.

Другие вопросы по тегам