WCF и SQL Server - как обрабатывать изменения данных? - PullRequest
3 голосов
/ 19 июля 2009

У меня есть приложение с такой архитектурой, как клиент / сервер / db . Связь между клиентом и сервером - WCF (перенесен из asmx), а база данных - SQL Server 2005.

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

Обновление заказа обычно выглядит так:

  1. Клиент читает заказ - изначально прочитанная копия сохраняется (сеанс) на сервере
  2. Клиент обновляет заказ - возвращает обновленный заказ на сервер
  3. Сервер снова прочитает заказ из базы данных и сравнит его с , первоначально прочитанным , чтобы проверить, был ли заказ изменен другим пользователем - в случае, если клиент получит уведомление о повторном чтении заказа
  4. Сервер сохранит изменения

Этот способ обработки изменения данных приводит к тому, что в определенный момент (3) на сервере будет 3 (разные) копии заказа в памяти! Кто-нибудь знает другую стратегию для этого?

Мы запускаем WCF с AspNetBackwardCompability, потому что нам нужна переменная Session для «хранения» первоначально прочитанных копий - это сделает мой день, если мы сможем сбросить этот

Ответы [ 4 ]

2 голосов
/ 19 июля 2009

Одно из решений заключается в том, чтобы при сохранении клиент предоставлял как первоначально прочитанные, так и обновленные значения. Тогда вам не нужна копия исходных значений в сеансе.

DataSets имеют встроенную возможность хранить обе версии (DataRowVersion.Original и DataRowVersion.Current), но для этого вам потребуется собственный метод (например, operationContract:

SaveMyData(MyType original, MyType updated);

Затем вы можете сохранить в базе данных таким образом:

UPDATE MyTable
SET Col1 = @NewCol1, Col2 = @NewCol2, ...
WHERE Col1 = @OldCol1, Col2 = @OldCol2, ...
IF @@ROWCOUNT = 0 ... update failed ...

Кроме того, в вашей таблице может быть столбец TIMESTAMP / ROWVERSION. Вы обращаетесь к клиенту и проверяете его при обновлении:

UPDATE MyTable
SET Col1 = @NewCol1, Col2 = @NewCol2, ...
WHERE PKCol = @PK AND TimeStampCol = @OldTimeStamp
IF @@ROWCOUNT = 0 ... update failed ...

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

1 голос
/ 19 июля 2009

Служба с отслеживанием состояния, например, реализованная вами, является антипаттерном большой службы. В качестве общего принципа веб-службы должны быть без сохранения состояния, иначе ваша масштабируемость может пострадать. Для оптимистической блокировки используйте столбец отметки времени для таблицы. Верните это как токен параллелизма клиенту, который возвращается без изменений, и сравните со значением в БД перед обновлением. Я работаю на сервере sql, но у oracle есть операторы select, которые могут вам помочь.

Если данные распределены по многим таблицам, рассмотрите подходящую стратегию блокировки с использованием хранимой процедуры.

1 голос
/ 19 июля 2009

Что должно предотвратить одновременное изменение после 3, до 4?

Обычный способ справиться с этим - исключить шаг 3 (в любом случае, он некорректен, если не выполняется повторяющийся уровень изоляции при чтении, и это полностью излишне), и применять изменения с оптимизмом, что ничего не изменилось (т. Е. Оптимистическая модель параллелизма). ). Чтобы убедиться, что действительно ничего не изменилось, вы используете либо предложение WHERE, содержащее все старые значения, либо добавляете в специальный столбец предложения WHERE, который изменяется при каждом обновлении, как отметка времени или строчная версия.

Если обновление было неактивным (оно не нашло старых значений), которые проверяются различными способами, например проверкой @@ ROWCOUNT или использованием предложения OUTPUT), тогда вы можете прочитать новые, измененные, значения и уведомления клиента, только в случае исключения.

0 голосов
/ 19 июля 2009

Мне нравится подход Джо - я тоже очень рекомендую.

При первоначальном чтении отправьте обратно значение столбца TIMESTAMP из основной таблицы клиенту либо в фактическом DataContract, либо в качестве заголовка в ответном сообщении WCF.

Если вы хотите обновить данные, отправьте это начальное значение метки времени с клиента на сервер. Затем сервер сначала проверит, изменилось ли это значение метки времени, и, если это так, выдает исключение FaultException, а не обновляет данные. Только если значение временной метки все еще совпадает со значением, возвращенным клиентом в вызове UPDATE, сервер фактически выполнит обновление.

Я бы порекомендовал использовать время данных SQL Server TIMESTAMP (кстати, на самом деле это не имеет ничего общего с датой и / или временем - на самом деле это просто уникальное, постоянно растущее число), потому что это намного больше точнее, чем DATETIME, и он автоматически обновляется SQL Server при каждой записи строки в таблицу. Создает идеальный «маркер» для проверки обновлений.

При таком подходе все, что вам нужно передать, это 8-байтовое значение метки времени - не нужно иметь три точные копии всей строки данных.

См. Эту замечательную статью " Понимание TIMESTAMP (ROWVERSION) в SQL Server ".

Марк

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...