简体   繁体   English

在asp.net中为用户锁定页面

[英]Locking the page in asp.net for a user

I am facing a problem with locking an asp.net page. 我正面临锁定asp.net页面的问题。 We have a user profile page in which we need to lock that page for the user who opened it first. 我们有一个用户个人资料页面,我们需要在该页面中为首先打开它的用户锁定该页面。 The details are as follows, There are many user profile records in database and we are passing the record number to querystring to open a particular page. 详细信息如下,数据库中有许多用户配置文件记录,我们将记录号传递给查询字符串以打开特定页面。 The user clicks a linkbutton in a grid and opens the record in readonly mode. 用户单击网格中的链接按钮并以只读模式打开记录。 There is an edit button which enables all the controls and makes it available to user once he clicks it. 有一个编辑按钮,可以启用所有控件,并在用户单击它后将其提供给用户。 The task is to lock the record to the user who clicks the edit button first. 任务是将记录锁定到首先单击编辑按钮的用户。

Apart from this there are many scenarios like the user may navigate from the page or he may close the page in between. 除此之外,还有许多场景,例如用户可以从页面导航,或者他可以在两者之间关闭页面。 In these cases the record should be available to the other users. 在这些情况下,记录应该可供其他用户使用。 Please give me some possible approaches or examples of how to solve the scenario. 请给我一些可能的方法或如何解决方案的例子。

Thanks in advance 提前致谢

I think this is a really bad idea for all the reasons you mentioned, but if I had to do this, what I would do is use the ASP.NET Cache. 我认为这是你提到的所有原因的一个非常糟糕的主意,但如果我必须这样做,我会做的是使用ASP.NET缓存。

So, something like this: 所以,像这样:

    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
    }

Then in the page you can do something like: 然后在页面中,您可以执行以下操作:

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

The nice thing here is that you automatically "unlock" the record after two minutes by using the built in ASP.NET cache. 这里的好处是你可以在两分钟后使用内置的ASP.NET缓存自动“解锁”记录。 So you get all that for free. 所以你可以免费获得所有这些。

If you try to lock a record, you are 100% going to get stale locks, which means you'll have to have a process to remove these stale locks. 如果您尝试锁定记录,则100%将获得过时的锁定,这意味着您必须有一个进程来删除这些过时的锁定。

If you are just trying to prevent concurrent modification, you can do this with a timestamp or a version field. 如果您只是想阻止并发修改,可以使用时间戳或版本字段来执行此操作。 When editing a record, put the version/timestamp into a hidden field. 编辑记录时,将版本/时间戳放入隐藏字段。 Then, when trying to apply updates, the first thing you do is re-read the data corresponding to the ID, then check the version that comes back from the database against the version in the hidden field. 然后,在尝试应用更新时,首先要做的是重新读取与ID对应的数据,然后根据隐藏字段中的版本检查从数据库返回的版本。 If they match, then it's safe to go ahead and apply the changes, and if it's not then you can apply some logic to determine how to proceed. 如果它们匹配,那么继续并应用更改是安全的,如果不匹配则可以应用一些逻辑来确定如何继续。

For a good user experience, I'd setup a heartbeat on the page after a user clicks "edit" using some javascript. 为了获得良好的用户体验,我在用户使用某些javascript点击“编辑”后在页面上设置了心跳。 Every 5 seconds or whatever is more reasonable I'd ping the server. 每隔5秒或者更合理的事情我会ping服务器。 So if the user disconnects for whatever reason you can release the lock pretty quickly by spinning off a thread that checks to see the last time the user pinged the page. 因此,如果用户因任何原因断开连接,您可以通过旋转检查上次用户ping页面的线程来快速释放锁定。 You'd have to store the ping time somewhere, like the server cache or maybe the session, but I'd prefer a distributed cache like memcache for load balancing (although it might not be important in your environment). 你必须在某个地方存储ping时间,比如服务器缓存或者会话,但我更喜欢像memcache这样的分布式缓存来实现负载平衡(尽管在你的环境中它可能并不重要)。

The lock itself should be fairly straightforward to implement, but I'd favor either distributed cache solution like memcache or a timestamp column in the database. 锁本身应该是相当简单的实现,但我喜欢分布式缓存解决方案,如memcache或数据库中的时间戳列。 I'd still include a failsafe expiration in case it doesn't expire through the heartbeat. 如果它没有通过心跳到期,我仍然会包含故障安全过期。

I've worked on a similar system and in our case we set a bit on the record in the database to indicate that it was being edited. 我已经在一个类似的系统上工作,在我们的例子中,我们在数据库中设置了一些记录,表明它正在被编辑。 However, unlike your case we were able to set the expectation with the end users of our system that they MUST click either 'Save' or 'Cancel' to flip the bit back over and allow editing of the record by other users. 但是,与您的情况不同,我们能够为系统的最终用户设置期望,他们必须单击“保存”或“取消”以翻转该位并允许其他用户编辑记录。 I'm honestly not totally sure how to handle the case in which a user abandons the page, though what comes to mind is to have a scheduled task that runs a sproc that will free the record if it has been locked for a certain period of time (this means you'd need a datetime field as well to indicate when the record was locked). 我真的不完全确定如何处理用户放弃页面的情况,尽管我想到的是有一个运行sproc的计划任务,如果它已被锁定一段时间,它将释放记录时间(这意味着您还需要一个日期时间字段来指示记录何时被锁定)。

It's not possible because it is not possible to determine whether the user's browser has closed. 这是不可能的,因为无法确定用户的浏览器是否已关闭。 You can do silly things like infer that the browser has closed by doing something like having the browser poll your website every minute (like a ping), but I wouldn't go that route. 你可以做一些愚蠢的事情,例如推断浏览器已关闭,例如让浏览器每分钟浏览一次你的网站(比如ping),但我不会那样做。 You could also use a slideExpiration on the session, but to me the session should be used for session and not for 'locking' pages. 您还可以在会话中使用slideExpiration,但对我来说,会话​​应该用于会话而不是“锁定”页面。

Maybe keep cached value of the page name in the Cache that keeps record of the user that has locked the file with an expiry of say 10 minutes. 也许保持缓存中的页面名称的缓存值,该值保存已锁定文件的用户的记录,例如10分钟的到期时间。 If another user requests the file, your code first checks to see if the page name is in the cache. 如果另一个用户请求该文件,您的代码首先检查页面名称是否在缓存中。 If the same user requests the same file (ie does a postback or a refresh) then update the cache (ie reset the timer) and they'll have an extra 10 minutes. 如果同一个用户请求相同的文件(即回发或刷新),则更新缓存(即重置计时器),他们将额外花费10分钟。 Let's say the user is writing a long winded letter and they haven't posted back or refreshed the page and 10 minutes is up, you could remedy this by having a client side timer that triggers after 9 minutes and alerts the user "your time with the page will end in 1 minute, click okay to extend your time for another 10 minutes". 假设用户正在写一封长信,他们没有回发或刷新页面,10分钟就到了,您可以通过让客户端计时器在9分钟后触发并通知用户“您的时间”来解决这个问题。页面将在1分钟后结束,单击“确定”将您的时间再延长10分钟“。

I'm not saying this is a great solution, but it's an idea. 我不是说这是一个很好的解决方案,但这是一个想法。 What I do know is that because you cannot tell if the browser is closed you'll need to think outside the box. 我所知道的是,因为你无法判断浏览器是否已经关闭,所以你需要在盒子外面思考。

@Brian Driscoll says they set a bit on the database record. @Brian Driscoll说,他们在数据库记录上做了一点。 Then they tell their users they MUST click save or cancel. 然后他们告诉用户他们必须点击保存或取消。 Really? 真? There are so many things wrong with this. 这有很多问题。

Rule 1 of interaction design "users" will not do what you expect - ever. 交互设计“用户”的规则1将无法实现您所期望的 - 永远。

Rule 1 of locking stuff - if the actor responsible for releasing the lock can fail it will. 锁定东西的规则1 - 如果负责释放锁的actor可能会失败它。 Even if you managed, through shock therapy, to train your user base their computer might fail, the database might fail, the connection might fail, the network might fail, the user might die in the middle of the edit. 即使您通过休克疗法管理您的用户群,他们的计算机可能会失败,数据库可能会失败,连接可能会失败,网络可能会失败,用户可能会在编辑过程中死亡。

Rule 2 of locking stuff - when rule 1 of locking stuff is in effect you need an out - specifically a TIMEout or some other way of releasing orphaned locks. 锁定东西的规则2 - 当锁定东西的规则1生效时,你需要一个out - 特别是TIMEout或其他一些释放孤立锁的方法。

There's a lot written about locking on WikiPedia that's worth reading. 有很多关于锁定WikiPedia的文章值得一读。 File locking and thread related locking (mutexes, semaphores, etc) are really similar to this problem and understanding how those work is a good starting place. 文件锁定和线程相关的锁定(互斥,信号量等)与这个问题非常相似,并且理解这些工作是一个很好的起点。 A user really is just another external parallel processing unit right? 用户真的只是另一个外部并行处理单元吗?

@Michael Yoon gives a very interesting answer in my opinion. @Michael Yoon在我看来给出了一个非常有趣的答案。 We're in the process of implementing exactly this type of page based locking and this is what we have the system doing now, but we'll change it to be more like Yoon's idea to constantly ping the server to extend the lock. 我们正在实现这种类型的基于页面的锁定,这就是我们系统现在所做的事情,但我们将其更改为更像Yoon的想法,不断ping服务器以扩展锁定。

  1. User clicks edit 用户点击编辑
  2. Request lock on item to be edited (duration 5 minutes?) acquired 请求锁定要编辑的项目(持续时间5分钟?)
    • On Success GOTO 3 关于成功GOTO 3
    • On Failure notify user lock not acquired 失败时通知未获取用户锁定
  3. Check the item's version returned by the lock token 检查锁定令牌返回的项目版本
    • If the version is the same (could never be older, right?) GOTO 5 如果版本相同(永远不会更老,对吗?)GOTO 5
    • If the version is newer GOTO 4 如果版本是较新的GOTO 4
  4. Retrieve the latest version of the item from the server 从服务器检索项目的最新版本
  5. Enter edit mode 进入编辑模式
  6. Periodically test the age of the lock and notify the user when the lock is about to expire 定期测试锁的年龄,并在锁即将到期时通知用户

Specifically we would replace item 6 with Yoon's idea to not bother the user with notification, but to use "micro-lock durations" of perhaps 30 seconds and keep the lock active by extending the lock duration from the client more often than 30 seconds. 具体来说,我们会用Yoon的想法替换第6项,不要通知用户,而是使用大约30秒的“微锁持续时间”,并通过从客户端延长锁定持续时间超过30秒来保持锁定活动。

That is a hard thing to do on the web. 这在网络上很难做到。 I recommend using a TimeStamp in the db when User1 accesses the data. 当User1访问数据时,我建议在数据库中使用TimeStamp。 Then give User1 a way to unlock on the page manually and also use a timeout value utilizing the TimeStamp to automatically un-lock the records for User2, if User1 abandons the page(navigates away, closes browser, looses internet connection...etc). 然后给User1手动解锁页面,并使用TimeStamp超时值自动解锁User2的记录,如果User1放弃页面(导航离开,关闭浏览器,丢失互联网连接......等) 。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM