Вставить после неудачного обновления, транзакция не блокируется - PullRequest
1 голос
/ 29 декабря 2011

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

CREATE TABLE dbo.[Settings]
(
  [Name] [nvarchar](100) NOT NULL,
  [Value] [nvarchar](4000) NOT NULL,
  CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED
  (
    [Name] ASC
  ) ON [PRIMARY]
) ON [PRIMARY]

Когда я сохраняю измененную настройку, язапустите транзакцию, попробуйте обновить значение, если обновление завершится неудачно (поскольку запись еще не существует), я вставлю новую запись:

// Begin Transaction

cmd.CommandText = "UPDATE dbo.[Settings] SET [Value] = @value WHERE [Name] = @name";
cmd.Parameters.AddWithValue("@value", value);
cmd.Parameters.AddWithValue("@name", name);
int cmdrc = cmd.ExecuteNonQuery();
if (cmdrc != 1)
{
  cmd.CommandText = "INSERT INTO dbo.[Settings] ([Name], [Value]) VALUES (@name, @value)";
  cmd.ExecuteNonQuery();
}

// Commit Transaction

Моя проблема: если два потока попытались сохранить и сохранитьЗначение для одного и того же имени в одно и то же время (что случается очень редко, но все же), будет конфликт:

Оба потока пытаются обновиться, оба потока получают cmdrc == 0, оба потока пытаются ивставьте новое значение, только одно будет успешным, другое получит SqlException.

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

Есть ли способ заставить SQL Server заблокироватьключевое слово, хотя для него не найдено ни одной записи?

1 Ответ

1 голос
/ 29 декабря 2011

Вы можете использовать

UPDATE dbo.[Settings] 
WITH (HOLDLOCK, UPDLOCK)
SET [Value] = @value WHERE [Name] = @name

Таким образом, блокировка удерживается, но зачем?Почему бы просто не игнорировать ошибку, так как она произвольна, которая все равно "победит".

...