最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How lock table editing with MS Access front, SQL Server back? - Stack Overflow

programmeradmin0浏览0评论

I am implementing pseudo-locking in an Access project. I have a table, tblLock which holds the lock records (includes the area being locked, the user, a timestamp).

I could simply rely on this table be the "gatekeeper" as it were, but it seems to me that there is a potential for race conditions.

Example:

  • User A opens recordset to add a new lock record for area Z
  • User B opens recordset to add a new lock record for area Z
  • Both check the recordset to see that the record is not currently locked
  • Both are then able to add lock records for area Z.

To solve this, when the application was MS Access only, I use the following to eliminate concurrent editing:

    Set rs = CurrentDb.OpenRecordset(stSQL, dbOpenDynaset, dbDenyWrite)

where stSQL is something like

SELECT * FROM tblLock WHERE [Area] = 'TestArea'

With this, only one user can make edits to tblLock at any given moment. User B in the above scenario cannot even open the RecordSet for editing tblLock. Thus the race condition is eliminated. I'm basically trying to use a coding pattern similar to Java's synchronization.

The problem, the presence of dbDenyWrite elicits the following error message:

ODBC--Cannot lock all records.

NOTE: for the version with SQL Server in the backend, the OpenRecordset line has been changed to read as follows:

    Set rs = CurrentDb.OpenRecordset(stSQL, dbOpenDynaset, dbSeeChanges + dbDenyWrite)    

It there a way to encode OpenRecordset so that it prevents concurrent editing of tblLock? It would be nice if concurrent ReadOnly access were still allowed to the table, but if necessary, I can drop that requirement.

I am implementing pseudo-locking in an Access project. I have a table, tblLock which holds the lock records (includes the area being locked, the user, a timestamp).

I could simply rely on this table be the "gatekeeper" as it were, but it seems to me that there is a potential for race conditions.

Example:

  • User A opens recordset to add a new lock record for area Z
  • User B opens recordset to add a new lock record for area Z
  • Both check the recordset to see that the record is not currently locked
  • Both are then able to add lock records for area Z.

To solve this, when the application was MS Access only, I use the following to eliminate concurrent editing:

    Set rs = CurrentDb.OpenRecordset(stSQL, dbOpenDynaset, dbDenyWrite)

where stSQL is something like

SELECT * FROM tblLock WHERE [Area] = 'TestArea'

With this, only one user can make edits to tblLock at any given moment. User B in the above scenario cannot even open the RecordSet for editing tblLock. Thus the race condition is eliminated. I'm basically trying to use a coding pattern similar to Java's synchronization.

The problem, the presence of dbDenyWrite elicits the following error message:

ODBC--Cannot lock all records.

NOTE: for the version with SQL Server in the backend, the OpenRecordset line has been changed to read as follows:

    Set rs = CurrentDb.OpenRecordset(stSQL, dbOpenDynaset, dbSeeChanges + dbDenyWrite)    

It there a way to encode OpenRecordset so that it prevents concurrent editing of tblLock? It would be nice if concurrent ReadOnly access were still allowed to the table, but if necessary, I can drop that requirement.

Share Improve this question edited Feb 6 at 4:50 marc_s 755k184 gold badges1.4k silver badges1.5k bronze badges asked Feb 5 at 19:37 Phil FreihofnerPhil Freihofner 7,9101 gold badge23 silver badges45 bronze badges 7
  • I suggest you handle this via SQL-Server stored procedures called from access. SQL-Server can then make use of Transactions to ensure only one user is able to set a lock record at a time. – Bart McEndree Commented Feb 5 at 19:44
  • 1 Transactions don't ensure one user at a time... that would be a table lock. – Dale K Commented Feb 5 at 19:50
  • Some suggest you create an index and simply handle the error when user A or B violates it. stackoverflow.com/questions/20971680/… – Bart McEndree Commented Feb 5 at 20:46
  • 2 If you have a unique index on the locking table rows, you can see which "side" wins and proceed accordingly, as they say, there can be only one. Darn, @BartMcEndree just beat me to it, shouldn't have wasted time quoting Highlander – siggemannen Commented Feb 5 at 20:49
  • I agree with Dale that transactions don't eliminate race conditions. At least, not in any way that I've been able to determine so far. I'm going to think through whether there's a way to make use of the lock table index. Unfortunately, it seems the answers on question 20971680 (from Bart) admit to permitting race conditions. (This is part of why I hate working with MS--with Java they've got concurrency nailed.) The whole point of adding SQL Server as the back end was to avoid Access kloojiness and occasional crashes. – Phil Freihofner Commented Feb 6 at 5:11
 |  Show 2 more comments

1 Answer 1

Reset to default 2

As suggested in the comments?

Just create the table locks, and add a unique index to the two columns you want to use for the lock.

Public Function Lockit(sForm As String, PK As Long) As Boolean

    Dim rstLock          As DAO.Recordset
    Dim strSQL           As String
   
    strSQL = "SELECT * FROM tblLocks WHERE Form = '" & sForm & "' AND " & _
             "PK = " & PK
    
    Set rstLock = CurrentDb.OpenRecordset(strSQL, dbOpenDynaset, dbSeeChanges)
    
    If rstLock.RecordCount <> 0 Then
       Lockit = False
    Else
       rstLock.AddNew
       rstLock!Form = sForm
       rstLock!PK = PK
       rstLock.Update
       Lockit = True
    End If

    rstLock.Close

End Function


Public Sub UnLockIt(sForm As String, PK As Long)

    Dim strSQL       As String
   
    strSQL = "DELETE FROM tblLocks WHERE Form = '" & sForm & "' AND PK = " & PK
    
    CurrentDb.Execute strSQL, dbSeeChanges
    

End Sub


Sub TestLock()


   Dim PK       As Long
   Dim sForm    As String
   
   PK = 123
   sForm = "frmHotels"
   
   Debug.Print Lockit(sForm, PK)
   
   
End Sub

So, since we have a unique index on the Form and PK?

Then no insert to the table is possible, and thus 2 records can never be added to the lock table as a result.

So, just create a index on the SQL table based on 2 columns, and set the index to unique.

So, adding the index looks like this:

And then setting index to unique:

So, you don't need to "really" lock any table, since SQL server will prevent two rows with the same "form" and "PK". Of course, you can change "Form" to "MyTable" or some such (table is a reserved word - so, don't use that).

I also have as a personal preference have the routine returning True if the lock was granted. I suppose some developers might prefer the reverse, but regardless, the above sample code is a proof of concept based on this simple idea of using a unique index on the two columns.

Now, it "might" be possible that 2 users enter the else condition (in which a lock was not found). I actually don't believe this will occur due to how SQL accepts requests from many users, but will sequential execute those requests.

If for some reason over time, you did have this occur?

You could I suppose add the following code in the lock routine to deal with this case (which I don't think will occur).

Hence, this:

    Else
        rstLock.AddNew
        rstLock!Form = sForm
        rstLock!PK = PK
        On Error Resume Next
        rstLock.Update
        If Err.Number = 0 Then
            Lockit = True
        Else
            Lockit = False
        End If
        On Error GoTo 0 ' turn off error handling
    End If

So, you could I suppose trap a failed insert with error handing, and also assume that is a failed lock. As noted, you probably don't need to add this extra precaution, but might as well.

发布评论

评论列表(0)

  1. 暂无评论