VBA for Excel 2016中的ADODB连接超时 - 如何检查连接是否仍处于活动状态?

时间:2017-10-02 13:14:27

标签: sql excel vba excel-vba adodb

我使用VBA开发了一个小型Excel插件,它直接连接到数据库。我通过DSN建立连接。插件打开并向右移动时,插件非常有效。但是,一段时间后,与数据库的连接似乎超时。更准确地说,完全有效的查询在尝试打开记录集时会返回错误。

我的代码是这样的:

'Check Connection
If Not MakeConnectionToDB Then
    'Connection failed
[do something]
        Exit Function
    End If
    'Active connection - run the query!
            If Rs Is Nothing Then Set Rs = New ADODB.Recordset  'make sure its an active object
            If Not Rs.State = adStateClosed Then Rs.Close       'make sure its not full (throws error if a query was called before)
            Rs.Open strSQLQuery, CON                            'Run query

如果应用程序已打开但未暂时使用,则rs.open语句将失败。这是尽管MakeConnectionToDB UDF,它看起来像这样:

If Not ConIsActive Then 'If there is no active connection, make it so
            If CON Is Nothing Then  'Connection may be inactive because the object dropped, or because it timed out, or any other reason - Only recreate the object if the former is the case
                Set CON = New ADODB.Connection
            End If
            On Error Resume Next
            CON.Open strCon         'Try to connect - on error resume statement in order to ignore a connection error, that will be caught below
            On Error GoTo 0
            Err.Clear
            MakeConnectionToDB = ConIsActive    'This is where a connection error will be caught if it occurred
        Else
            MakeConnectionToDB = True 'connection is active already
        End If

和ConIsActive看起来像:

Private Function ConIsActive() As Boolean
    'return TRUE if there is an active connection, false otherwise
    Dim blnTemp As Boolean
    blnTemp = False
    If (Not (CON Is Nothing)) And (Not (CON = "")) Then If CON.State = adStateOpen Then blnTemp = True
    ConIsActive = blnTemp
End Function

基本上,我检查连接是否打开。我的问题:所有这些检查都返回TRUE,但连接根本没有打开。如果我连接,然后离开应用程序一段时间,然后返回到它,以上所有将返回连接处于活动状态,但是当尝试使用新查询打开记录集时它将失败,可能是因为服务器关闭了连接什么的。我需要找到一种方法来检查连接是否真的能够打开记录集。

我可以ping服务器吗?如何检查数据库是否实际将结果返回给我的查询?是否有一种方法具有比仅仅将测试查询发送到服务器以及记录集上的错误处理更高的性能?我想这可行,但我需要一个高性能的解决方案,我不认为将简单连接检查的查询数量增加一倍是一个很好的解决方案......

感谢任何帮助!

1 个答案:

答案 0 :(得分:2)

您的CON对象似乎是全局范围的,打开一次,然后在代码中的任何地方使用,并且可能在某个时候关闭......或者不是。

与任何支持对象的语言编写的代码库中的每个对象一样,数据库连接应该尽可能短。

你打开它,你做你需要做的事情,然后你关闭它。如果你不知道下一个命令将对它执行什么以及何时执行,那么连接就没有剩余的业务。

删除全局范围CON。用火杀死它。连接应该是使用它的函数或过程的本地连接 - 它从该范围开始,并以该范围结束。

或者您可以封装在您自己的对象中,如果这样可以让您更轻松。

'@Folder("Data.SqlConnection")
Option Explicit
Private Const CONNECTION_STRING As String = "{CONNECTION STRING}"
Private mConnection As ADODB.Connection

Private Sub Class_Initialize()
    Set mConnection = New ADODB.Connection
    mConnection.Open
End Sub

Private Sub Class_Terminate()
    mConnection.Close
    Set mConnection = Nothing
End Sub

Public Sub ExecuteNonQuery(ByVal sql As String, ParamArray params())
    With New ADODB.Command
        Set .ActiveConnection = mConnection
        Dim p As ADODB.Parameter
        For Each p In params
            .Paramaters.Append p
        Next
        .Execute
    End With
End Sub

'...

SqlConnection类的实例也应该尽可能短暂,但现在大部分的管道被抽象掉了,所以你的调用代码看起来像这样:

 Const sql As String = "exec dbo.LogUserIn @userName=?, @password=?;"
 With New SqlConnection

     Dim userName As ADODB.Parameter
     Set userName = .CreateStringParameter(Environ$("USERNAME"))

     Dim password As ADODB.Parameter
     Set password = .CreateStringParameter(PromptForPassword)

     .ExecuteNonQuery sql, userName, password

 End With

连接始于New SqlConnection,干净地结束于End With,您可以根据需要调整SqlClass,以支持交易,和/或如上所示,抽象出来参数创建样板。

但是这个想法仍然存在:你没有创建一个数据库连接并让它在全球范围内悬空,不知道某些代码是否可能将其设置为Nothing,或者关闭它,或者启动了从未犯过的交易,或者上帝知道什么。

  1. 创建
  2. 打开
  3. 执行
  4. 关闭
  5. 始终。尽可能紧凑。然后你就不会有任何对象生命周期问题。