views:

64

answers:

6

I have a system set up to lock certain content in a database table so only one user can edit that content at a time. Easy enough and that part is working fine. But now I'm at a road block of how to send a request to "unlock" the content. I have the stored procedure to unlock the content, but how/where would I call it when the user just closes their browser?

+5  A: 

You also can't know when the user turns off his computer. You have to do it the other way around.

Require that the lock be renewed periodically. Only the web site would do the periodic renewal. If the user stops using the web site, then the lock expires.

Otherwise, require the user to explicitly unlock the content. Other users who want to edit the content can then go yell at the first user when they can't do their jobs. Not a technological solution, but still a good one. Shame works.

John Saunders
Hmm. I was afraid that was going to be the answer. I wouldn't be worried but it seems the average IQ of a user is below 70 :(
Jisaak
@Jisaak: like I said, shame works. Make them stand up in front of the next meeting to say how they blocked the work of other people.
John Saunders
Ahahaha, I'm thinking I'll be starting a money jar. So every time someone forgets to unlock they have to put a dollar in my money jar. Nothing like a free meal :)
Jisaak
@Jisaak: actually, I'm sure there's research on this, but I bet a money jar is less effective than the disapproval of one's coworkers.
John Saunders
+1  A: 

The best thing you can really do is add something to your Session_End in your global.asax. Unfortunately, this won't fire until the session times out.

When the user clicks the "X" in their browser, there isn't anyway to guarantee the browser will send you anything back.

AaronS
But regardless the session_end will still be called eventually when the user times out, correct?
Jisaak
Correct, unless something else stops the full IIS process itself.
AaronS
A: 

There is such event as "user closes browser".

Nevertheless, I can think of two workarounds:

  • Use Javascript/Ajax to permanently (lets say every 10 seconds) call a method in your page. The DateTime of your last query needs to be stored somewhere. Now you write a windows service that checks every second which session are timed out. Perform your custom action there.
  • Use the global.asax Session_End() -Event. (cannot be used with every SessionState, look up for which ones it is usable)
citronas
I wouldn't want to rely on any sort of client-side "user closes browser" event for anything that affects the server or other users.
tehblanx
A: 

Trying to leave a stackoverflow answer page pops up an "are you sure" dialog. Perhaps during the on-page-leave event that SO uses (or however SO does this), you can send a final request with an XmlHttpRequest object. This won't cover if the browser process closes unexpectedly (use session_onend for that), but it will at least send the "I'm closed" event earlier

Sukasa
+1  A: 

A quick note on the Session_End approaches. If you use this method, then you have to ensure

  1. That sessionstate is InProc, eg. add something like this to your Web.config

    <sessionState mode="InProc" timeout="timeout_in_minutes"/>

  2. Make sure that you've setup IIS as to not recycle worker processes during normal operation (see for instance this blog post).

Edit: Not directly answering the question directly, but another approach would be to use Optimistic concurrency control on the data in question.

Mads Ravn
Oh dang. Thanks for the heads up. So much for implementing a quick lock feature and having a nice weekend.
Jisaak
No problem. I've been hit by 2. myself and I wouldn't want it to happen to anyone else :)
Mads Ravn
A: 

I think your one stored procedure can do the locking and unlocking (used with "Select @strNewMax As NewMax")...

Here is an example from a system I have:

Declare @strNewMax Char
Select @strNewMax = 'N'

BEGIN TRANSACTION

/* Lock only the rows for this Item ID, and hold those locks throughout the transaction. */
If @BidAmount > (Select Max(AB_Bid_AMT) from AuctionBid With(updlock, holdlock) Where AB_AI_ID = @AuctionItemId)
Begin
    Insert Into AuctionBid (AB_AI_ID, AB_Bid_AMT, AB_Emp_ID, AB_Entry_DTM)
    Select @AuctionItemId, @BidAmount, @EmployeeId, GetDate()
    Select @strNewMax = 'Y'
End

COMMIT TRANSACTION

Select @strNewMax As NewMax

This will insert a record as the next highest bid, all while locking the entire table, so no other bids are processed at the same time. It will return either a 'Y' or 'N' depending on if it worked or not.

Maybe you can take this and adjust it to fit your application.

Mikey