У меня есть ситуация, где два человека могли бы работать над тем же порядком (сохраненный в базе данных SQL MS) от двух различных компьютеров. Для предотвращения потери данных в случае, где можно было бы сохранить его копию порядка сначала и затем немного позже, второе сохранит его копию и перезапишет первое, я добавил проверку по сравнению с lastSaved полем (дата и время) перед сохранением.
Код примерно походит на это:
private bool orderIsChangedByOtherUser(Order localOrderCopy)
{
// Look up fresh version of the order from the DB
Order databaseOrder = orderService.GetByOrderId(localOrderCopy.Id);
if (databaseOrder != null &&
databaseOrder.LastSaved > localOrderCopy.LastSaved)
{
return true;
}
else
{
return false;
}
}
Это работает большую часть времени, но я нашел одну небольшую ошибку.
Если orderIsChangedByOtherUser возвратит false, то локальной копии обновят ее lastSaved к текущему времени и затем сохраняется к базе данных. Значение lastSaved в локальной копии и DB должно теперь быть тем же. Однако, если orderIsChangedByOtherUser выполняется снова, он иногда возвращает true даже при том, что никакой другой пользователь не внес изменения в DB.
При отладке в Visual Studio, databaseOrder. LastSaved и localOrderCopy. LastSaved, кажется, имеют то же значение, но выглядя ближе они несколько раз отличаются несколькими миллисекундами.
Я нашел эту статью с коротким уведомлением на точности миллисекунды для даты и времени в SQL:
Другая проблема состоит в том, что SQL Server снабжает ДАТУ И ВРЕМЯ точностью 3,33 миллисекунд (0. 00333 секунды).
Решение, о котором я мог думать для этой проблемы, состоит в том, чтобы сравнить два datetimes и считать их равными, если они отличаются меньше, чем говорить 10 миллисекунд.
Мой вопрос Вам затем: там какие-либо лучшие/более безопасные пути состоят в том, чтобы сравнить два значения даты и времени в MS SQL, чтобы видеть, являются ли они точно тем же?
Я знаю, что вы сказали, что не можете изменить тип, но если это необходимо только для поддержания совместимости и использования 2008 года, вы можете изменить поле lastSaved
на DATETIME2
(т.е. полностью совместим с DATETIME
) и использует SYSDATETIME ()
, оба из которых имеют гораздо большую точность.
Пока вы находитесь в SQL 2005 и ранее, проблема точности всегда будет присутствовать, никогда не будет точнее 1/300 секунды, или 3,33 мс.
Независимо от недостатка точности вы программируете несовершенное состояние гонки, когда оба пользователя могут записывать в базу данных в быстрой последовательности, но оба будут считаться успешными. Отсутствие точности увеличивает вероятность того, что это произойдет, если только проверка и последующая запись происходят в течение тех же 3-4 мс.
Любая попытка проверки с последующей записью страдает от этой проблемы, и вам придется либо принять последствия оптимистической блокировки, либо изменить блокировку на пессимистическую, либо реализовать какую-то стратегию типа семафора для правильной обработки блокировки.
Вы можете использовать поле временной метки для проверки даты последнего редактирования, а не поле времени? (В SQL 2008 это теперь RowVersion)
Вы должны убедиться, что ваши временные точности совпадают - в основном это можно сделать, имея правильную логику на стороне C #, чтобы фактически снизить точность при является встроенным в объекте DateTime - в основном сделайте так, чтобы у вас были, например, временные метки всегда в секундах, а не ниже, на всех уровнях.
Если вы сделаете это правильно, отметки времени на всех уровнях будут немедленно сопоставимы.
Вы можете добавить поле целочисленной версии в таблицу заказов. Каждый раз, когда пользователь сохраняет заказ, вы увеличиваете ревизию на единицу. Затем легко проверить, изменил ли кто-то заказ или пользователь, который хочет сохранить заказ, находится на последней версии.