【问题标题】:How to deal with Excel ADO connection concurrency?Excel ADO 连接并发如何处理?
【发布时间】:2021-04-20 11:52:48
【问题描述】:

我在 Excel 上制作了一个多用户界面,以便使用 ADO Connection 在一个常见的 Excel 文件中写入记录。

我有四个用户在使用这个界面(每个用户使用不同的界面文件)。
它一直有效,直到两个或更多用户尝试同时添加或编辑一条记录。
在这种情况下,VBA 会在尝试为最后一个用户打开 ADO 连接后立即以只读模式打开 Excel 文件。

已打开只读 Excel 文件

我的解决方法是使用 ISWORKBOOKOPEN() 函数来识别何时发生并发,然后关闭文件,关闭连接,等待一秒钟,然后重试:

Application.ScreenUpdating = False

Dim ADODBCONNECT As New ADODB.Connection
Dim RECORDSET As New ADODB.RECORDSET
Dim SQL_FILTER As String
Dim DATABASE As String

' -------- BACKUP --------

Dim oFSO As Object
Set oFSO = CreateObject("Scripting.FileSystemObject")
Call oFSO.CopyFile(Range("SLITTING_DATABASE"), Range("BACKUP_FILENAME"))

' -------- ADIÇÃO DO REGISTRO --------

DATABASE = Workbooks("S41 - Deacro.xlsm").Sheets("DATA SOURCES").Range("SLITTING_DATABASE")

OPEN_CONN:

ADODBCONNECT.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source ='" & DATABASE & "'; 
Extended Properties='Excel 12.0'"
ADODBCONNECT.Open

If ISWORKBOOKOPEN(DATABASE) = True Then

    ADODBCONNECT.Close
    
    Workbooks(DATABASE).Close False
    
    newHour = Hour(Now())
    newMinute = Minute(Now())
    newSecond = Second(Now()) + 1
    WAITTIME = TimeSerial(newHour, newMinute, newSecond)
    Application.Wait WAITTIME
    
    GoTo OPEN_CONN
    
End If

SQL_FILTER = "Select * From [SLIT_INCOME$]"
RECORDSET.Open SQL_FILTER, ADODBCONNECT, adOpenKeyset, adLockOptimistic

RECORDSET.AddNew

RECORDSET!SlittingWO = Workbooks("S41 - Deacro.xlsm").Sheets("INTERFACE").Range("OP_Corte")
RECORDSET!ReelUD = CADASTRO_DE_BOBINAS.REEL_UD_ENTRY
RECORDSET!ReelLenghtm = CADASTRO_DE_BOBINAS.REEL_MTS_IN_ENTRY
RECORDSET!Date = Now

RECORDSET.UPDATE

RECORDSET.Close
ADODBCONNECT.Close

' -------- ATUALIZAÇÃO DOS DADOS NA INTERFACE --------

Call UPDATE_DATA

Application.ScreenUpdating = True

这里还有两个问题:

1 - 当文件打开时,workbooks.close 似乎不起作用,原因是:

错误9,下标超出范围

2 - 如果在打开 ADO 连接后使用该函数,即使该连接已被检查文件可用性的同一用户打开,它也会返回 true。

我在这里复制了另一个问题的函数:

Function ISWORKBOOKOPEN(FileName As String)

    Dim ff As Long, ErrNo As Long

    On Error Resume Next
    ff = FreeFile()
    Open FileName For Input Lock Read As #ff
    Close ff
    ErrNo = Err
    On Error GoTo 0

    Select Case ErrNo
    Case 0:    ISWORKBOOKOPEN = False
    Case 70:   ISWORKBOOKOPEN = True
    Case Else: Error ErrNo
    End Select
    
End Function

有没有办法检测到文件正在被另一个用户通过 ADO 连接访问,所以我可以做一个循环来强制最后一个用户等到第一个用户的连接关闭?

我知道 Excel 在用作数据库文件时有很多限制,但它是我公司提供的唯一工具。

【问题讨论】:

    标签: sql excel vba concurrency ado


    【解决方案1】:

    解决方法:创建了一个“锁定标志”文件,该文件作为输入打开,并且在 ADO 连接建立之前锁定了读取模式。第一个用户打开文件会导致第二个用户在 VBA 错误 70 上崩溃,我用它来循环第二个用户尝试打开“锁定标志”文件,直到第一个用户关闭它。

    Application.ScreenUpdating = False
    
    Dim ADODBCONNECT As New ADODB.Connection
    Dim RECORDSET As New ADODB.RECORDSET
    Dim SQL_FILTER As String
    Dim DATABASE As String
    
    ' -------- BACKUP --------
    
    Dim oFSO As Object
    Set oFSO = CreateObject("Scripting.FileSystemObject")
    Call oFSO.CopyFile(Range("SLITTING_DATABASE"), Range("BACKUP_FILENAME"))
    
    ' -------- BLOQUEAR BANCO DE DADOS --------
    
    DATABASE = Workbooks("S41 - Deacro.xlsm").Sheets("DATA SOURCES").Range("SLITTING_DATABASE")
    LOCKING_FLAG_FILE = Workbooks("S41 - Deacro.xlsm").Sheets("DATA SOURCES").Range("LOCKING_FLAG_FILE")
    
    ErrNo = 0
    
    Do
    
        On Error Resume Next
        ff = FreeFile()
        Open LOCKING_FLAG_FILE For Input Lock Read As #ff
        ErrNo = Err
        On Error GoTo 0
    
    Loop While ErrNo = 70
    
    ' -------- ADIÇÃO DO REGISTRO --------
    
    ADODBCONNECT.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source ='" & DATABASE & "'; Extended Properties='Excel 12.0'"
    ADODBCONNECT.Open
    
    SQL_FILTER = "Select * From [SLIT_INCOME$]"
    RECORDSET.Open SQL_FILTER, ADODBCONNECT, adOpenKeyset, adLockOptimistic
    
    RECORDSET.AddNew
    
    RECORDSET!SlittingWO = Workbooks("S41 - Deacro.xlsm").Sheets("INTERFACE").Range("OP_Corte")
    RECORDSET!ReelUD = CADASTRO_DE_BOBINAS.REEL_UD_ENTRY
    RECORDSET!ReelLenghtm = CADASTRO_DE_BOBINAS.REEL_MTS_IN_ENTRY
    RECORDSET!Date = Now
    
    RECORDSET.UPDATE
    
    RECORDSET.Close
    ADODBCONNECT.Close
    
    ' -------- DESBLOQUEAR BANCO DE DADOS --------
    
    Close ff
    
    ' -------- ATUALIZAÇÃO DOS DADOS NA INTERFACE --------
    
    Call UPDATE_DATA
    
    Application.ScreenUpdating = True
    

    它远不是一个优雅的解决方案,但如果 excel 是您拥有的唯一工具,并且使用该界面的用户并不多(在这种情况下,我只有四个),这将完成这项工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-21
      • 2017-02-28
      • 1970-01-01
      相关资源
      最近更新 更多