Блокировка страницы в asp.net для пользователя

Я столкнулся с проблемой блокировки страницы asp.net. У нас есть страница профиля пользователя, на которой нам нужно заблокировать эту страницу для пользователя, который открыл ее первым. Подробности следующие: в базе данных много записей профилей пользователей, и мы передаем номер записи в строку запроса, чтобы открыть определенную страницу. Пользователь нажимает кнопку ссылки в сетке и открывает запись в режиме только для чтения. Существует кнопка редактирования, которая включает все элементы управления и делает их доступными для пользователя, когда он нажимает на нее. Задача состоит в том, чтобы заблокировать запись для пользователя, который первым нажмет кнопку редактирования.

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

заранее спасибо


person user545359    schedule 29.08.2011    source источник
comment
Что следует учитывать: сбои браузера, тайм-ауты сеанса, уход пользователя со стола, кнопка «Назад» и т. д.   -  person Devin Burke    schedule 29.08.2011


Ответы (7)


Я думаю, что это действительно плохая идея по всем причинам, которые вы упомянули, но если бы мне пришлось сделать это, я бы использовал кэш ASP.NET.

Итак, что-то вроде этого:

    Cache.Add(someUniqueKeyForAUserProfile, theUserThatLockedTheRecord, null, 
    DateTime.Now.AddSeconds(120), Cache.NoSlidingExpiration, CacheItemPriority.Normal, 
    UnlockRecord)

    private static void UnlockRecord(string key, object value, CacheItemRemovedReason reason) {
       //This particular record went longer than 2 minutes without 
       //the user doing anything, do any additional cleanup here you like
    }

Затем на странице вы можете сделать что-то вроде:

if (Cache[someUniqueKeyForAUserProfile] != theUserThatLockedTheRecord){
  //Tell the user they can't access the page
}

Приятно то, что вы автоматически «разблокируете» запись через две минуты, используя встроенный кэш ASP.NET. Таким образом, вы получаете все это бесплатно.

person aquinas    schedule 29.08.2011
comment
Что делать, если вы находитесь на веб-ферме? - person rick schott; 29.08.2011
comment
В качестве предостережения я должен упомянуть, что это будет работать, только если у вас есть один веб-сервер. Если у вас есть несколько серверов, вам нужно будет сохранить тот факт, что страница заблокирована в другом месте (предположительно, в базе данных), а затем вы должны проверить отметку времени, когда запись была заблокирована. - person aquinas; 29.08.2011
comment
Не пытаясь вас обидеть, это было только для других, которые приходят и не знают, во что они могут ввязываться. - person rick schott; 29.08.2011
comment
Вовсе нет, я написал свое предостережение еще до того, как увидел ваше сообщение. Вы совершенно правильно указываете на это. Это один подход, который допустим в определенных обстоятельствах. - person aquinas; 29.08.2011

Если вы попытаетесь заблокировать запись, вы со 100% вероятностью получите устаревшие блокировки, а это значит, что вам потребуется процесс для удаления этих устаревших блокировок.

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

person chris    schedule 29.08.2011

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

Сама блокировка должна быть довольно простой в реализации, но я бы предпочел либо решение с распределенным кэшем, такое как memcache, либо столбец меток времени в базе данных. Я бы все же включил отказоустойчивый срок действия на случай, если он не истечет через сердцебиение.

person Michael Yoon    schedule 31.08.2011

Я работал в похожей системе, и в нашем случае мы установили бит для записи в базе данных, чтобы указать, что она редактируется. Однако, в отличие от вашего случая, мы смогли установить ожидание от конечных пользователей нашей системы, что они ДОЛЖНЫ нажать «Сохранить» или «Отмена», чтобы перевернуть бит обратно и разрешить редактирование записи другими пользователями. Я, честно говоря, не совсем уверен, как справиться со случаем, когда пользователь покидает страницу, хотя мне приходит на ум запланированная задача, которая запускает sproc, который освобождает запись, если она была заблокирована в течение определенного периода времени. время (это означает, что вам также понадобится поле даты и времени, чтобы указать, когда запись была заблокирована).

person Brian Driscoll    schedule 29.08.2011

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

Возможно, сохранить кешированное значение имени страницы в кеше, в котором хранится запись пользователя, заблокировавшего файл, с истечением срока действия, скажем, 10 минут. Если другой пользователь запрашивает файл, ваш код сначала проверяет, находится ли имя страницы в кеше. Если тот же пользователь запрашивает тот же файл (т. е. выполняет обратную передачу или обновление), обновите кеш (т. е. сбросьте таймер), и у него будут дополнительные 10 минут. Допустим, пользователь пишет длинное письмо, и он не отправил ответ или не обновил страницу, а 10 минут истекли, вы можете исправить это, установив таймер на стороне клиента, который срабатывает через 9 минут и предупреждает пользователя «ваше время с страница завершится через 1 минуту, нажмите «ОК», чтобы продлить время еще на 10 минут».

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

person Chaos    schedule 31.08.2011

@ Брайан Дрисколл говорит, что они установили бит в записи базы данных. Затем они говорят своим пользователям, что они ДОЛЖНЫ нажать «Сохранить» или «Отменить». Действительно? В этом так много неправильного.

Правило 1 дизайна взаимодействия «пользователи» никогда не будут делать то, что вы ожидаете.

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

Правило 2 блокировки вещей - когда действует правило 1 блокировки вещей, вам нужен выход - в частности, TIMEout или какой-либо другой способ освобождения потерянных блокировок.

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

@Майкл Юн дает очень интересный ответ, на мой взгляд. Мы находимся в процессе реализации именно такого типа блокировки на основе страниц, и это то, что у нас сейчас делает система, но мы изменим ее, чтобы она больше соответствовала идее Юна постоянно пинговать сервер для продления блокировки.

  1. Пользователь нажимает изменить
  2. Request lock on item to be edited (duration 5 minutes?) acquired
    • On Success GOTO 3
    • При сбое уведомлять о том, что блокировка пользователя не получена
  3. Check the item’s version returned by the lock token
    • If the version is the same (could never be older, right?) GOTO 5
    • Если версия новее, ПЕРЕЙДИТЕ К 4
  4. Получить последнюю версию элемента с сервера
  5. Войти в режим редактирования
  6. Периодически проверяйте возраст блокировки и уведомляйте пользователя, когда истечет срок действия блокировки.

В частности, мы бы заменили пункт 6 на идею Юна, чтобы не беспокоить пользователя уведомлением, а использовать «длительность микроблокировки», возможно, 30 секунд, и поддерживать активную блокировку, продлевая продолжительность блокировки от клиента чаще, чем 30 секунд.

person Mark Lauter    schedule 22.12.2011

Это сложно сделать в сети. Я рекомендую использовать TimeStamp в db, когда User1 обращается к данным. Затем дайте Пользователю 1 способ разблокировать страницу вручную, а также используйте значение тайм-аута с использованием метки времени для автоматической разблокировки записей для Пользователя 2, если Пользователь 1 покидает страницу (уходит, закрывает браузер, теряет подключение к Интернету и т. д.). .

person rick schott    schedule 29.08.2011