检查ADODB连接是否打开。

21

我在一些Excel过程中使用以下代码来建立与我们数据库的连接。

Private Const strConn As String = _
    "PROVIDER=SQLOLEDB.1 ..."     

Sub OpenConnection()

Set cn = CreateObject("ADODB.Connection")
cn.Open strConn
cn.CommandTimeout = 0
Set rs = CreateObject("ADODB.Recordset")
Set rs.ActiveConnection = cn

End Sub 

在后续的代码中,我使用不同的SQL字符串打开连接。
我想测试rs是否已经打开,以便知道需要关闭它,但是以下代码无法实现。如何更改以下条件使其可行?

If (rs.Open = True) Then
    rs.Close
End If

以下方法可以工作,但我不想以这种方式使用错误陷阱:

On Error Resume Next
    rs.Close
3个回答

36

ADO Recordset具有.State属性,您可以检查其值是否为adStateClosedadStateOpen

If Not (rs Is Nothing) Then
  If (rs.State And adStateOpen) = adStateOpen Then rs.Close
  Set rs = Nothing
End If

关于 State 属性的 MSDN 文档

编辑: 不建议将 .State 与 1 或 0 进行比较,因为即使它在99.99%的情况下都有效,仍然有可能存在设置了其他标志的情况,这将导致 if 语句中的 adStateOpen 检查失败。

编辑2: 如果没有引用 ActiveX 数据对象,可以使用来自 ObjectStateEnum 的 adStateOpen 常量值进行后期绑定。

If Not (rs Is Nothing) Then
  If (rs.State And 1) = 1 Then rs.Close
  Set rs = Nothing
End If

你可以自己定义常量,以使你的代码更易读(定义所有常量的示例很好)。

Const adStateClosed As Long = 0 'Indicates that the object is closed.
Const adStateOpen As Long = 1 'Indicates that the object is open.
Const adStateConnecting As Long = 2 'Indicates that the object is connecting.
Const adStateExecuting As Long = 4 'Indicates that the object is executing a command.
Const adStateFetching As Long = 8 'Indicates that the rows of the object are being retrieved.    

[...]

If Not (rs Is Nothing) Then

    ' ex. If (0001 And 0001) = 0001 (only open flag) -> true
    ' ex. If (1001 And 0001) = 0001 (open and retrieve) -> true
    '    This second example means it is open, but its value is not 1
    '    and If rs.State = 1 -> false, even though it is open
    If (rs.State And adStateOpen) = adStateOpen Then 
        rs.Close
    End If

    Set rs = Nothing
End If

1
这是位运算的 And 操作。只有当 .State 中的那个位被设置并且所有其他位都被清零时,才会保留 adStateOpen 位。将 And 操作值与 adStateOpen 进行比较的结果只有在 .State 中设置了该位标志时才会为 True - Raybarg
嘿,你的困惑可能是有道理的。我不是指使用 rs.adStateOpen 而是 adStateOpen - Raybarg
我正在使用后期绑定,所以adStateOpen会出现“变量未定义”的错误...这就是为什么我编辑并添加了rs.的原因...尽管我认为rs是错误的编辑 - 我已经重新编辑过了。 - whytheq
1
不,就像我说的,在大多数情况下它会起作用,但有可能你最终会同时打开和执行命令或获取行,在这种情况下,.State值将是1+4=5(二进制0101)1+8=9(二进制1001)。如果您愿意,可以使用简单的If rs.State = 1,但为什么不按正确的方式做呢?这将在所有情况下都起作用,避免未来难以找到的错误情况。 - Raybarg
1
是的,ADODB.Connection.State 也使用 ObjectStateEnum - Raybarg
显示剩余5条评论

5
这是一个老话题,但如果还有人在寻找解决方法...在解除连接事件后,我遇到了一些麻烦。在全局对象中保存的打开的 db 连接会出现错误,即使重新连接到网络也是如此。这是由于远程主机强行终止了 TCP 连接所致。(错误-2147467259:TCP 提供程序:现有的连接被远程主机强行关闭。) 但是,错误只会在第一次尝试事务之后显示出来。在这一点上,无论是 Connection.State 还是 Connection.Version (根据上面的解决方案) 都不会显示任何错误。因此,我编写了下面的小子例程来强制发生错误 - 希望对您有用。我的设置(访问 2016,SQL Svr 2008R2)上的性能测试大约为每次调用 0.5ms。
Function adoIsConnected(adoCn As ADODB.Connection) As Boolean

    '----------------------------------------------------------------
    '#PURPOSE: Checks whether the supplied db connection is alive and
    '          hasn't had it's TCP connection forcibly closed by remote
    '          host, for example, as happens during an undock event
    '#RETURNS: True if the supplied db is connected and error-free, 
    '          False otherwise
    '#AUTHOR:  Belladonna
    '----------------------------------------------------------------

    Dim i As Long
    Dim cmd As New ADODB.Command

    'Set up SQL command to return 1
    cmd.CommandText = "SELECT 1"
    cmd.ActiveConnection = adoCn

    'Run a simple query, to test the connection
    On Error Resume Next
    i = cmd.Execute.Fields(0)
    On Error GoTo 0

    'Tidy up
    Set cmd = Nothing

    'If i is 1, connection is open
    If i = 1 Then
        adoIsConnected = True
    Else
        adoIsConnected = False
    End If

End Function

1

这个话题已经老旧了,但如果像我一样的其他人在寻找解决方案,这是我找到的一个解决方案:

Public Function DBStats() As Boolean
    On Error GoTo errorHandler
        If Not IsNull(myBase.Version) Then 
            DBStats = True
        End If
        Exit Function
    errorHandler:
        DBStats = False  
End Function

"myBase"是一个数据库对象,我已经创建了一个类来访问数据库(包括插入、更新等功能),在模块中使用该类声明一个对象(显然),并且我可以通过"[the Object].DBStats"测试连接:

Dim BaseAccess As New myClass
BaseAccess.DBOpen 'I open connection
Debug.Print BaseAccess.DBStats ' I test and that tell me true
BaseAccess.DBClose ' I close the connection
Debug.Print BaseAccess.DBStats ' I test and tell me false

编辑:在 DBOpen 中我使用“OpenDatabase”,在 DBClose 中我使用“.Close”和“set myBase = nothing”。 编辑2:在函数中,如果没有连接,“.version”会给你一个错误,所以如果没有连接,错误处理程序会给你 false。

这是什么:.Version?你能否添加到myClass内部的代码?我最近发布了这个问题,就在上周,所以我非常想看看你是如何编写myClass的:http://codereview.stackexchange.com/questions/116253/class-module-to-wrap-up-classic-ado-call-to-sql-server - whytheq
".Version"会给你数据库的版本号,所以如果你没有连接到数据库(我使用Access),你就无法知道版本号,因此通过这种方式,你可以知道是否已连接到数据库。 - JustGuest
这将我拉回了正轨,但在远程主机终止连接的情况下不起作用,因为版本仍然可用且非空。在这些情况下,可以使用我刚才发布的解决方案。 - Belladonna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接