Excel 2016 的 VBA 中的 ADODB 连接超时 - 如何检查连接是否仍处于活动状态?
ADODB Connection Timeout in VBA for Excel 2016 - how to check if a connection is still active?
我使用 VBA 开发了一个小型 Excel 插件,它直接连接到数据库。我通过 DSN 建立了连接。打开插件并直接使用它时,它的效果非常好。但是,过了一会儿,与数据库的连接似乎超时了。更准确地说,完全有效的查询 return 在尝试打开记录集时出错。
我的代码是这样的:
'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
基本上,我检查连接是否打开。我的问题:所有这些检查 return TRUE,但连接根本没有打开。如果我连接,然后离开应用程序一段时间,然后返回它,以上所有内容都会 return 连接处于活动状态,但是当尝试使用新查询打开记录集时它将失败,大概是因为服务器关闭了连接或其他东西。我需要找到一种方法来检查连接是否真的能够打开记录集。
我可以ping 服务器什么的吗?如何检查数据库是否确实 return 是我查询的结果?有没有一种方法比仅向服务器发送测试查询并结合记录集上的错误处理具有更高的性能?我想那行得通,但我需要一个高性能的解决方案,而且我认为将简单连接检查的查询数量加倍并不是一个更好的解决方案...
感谢任何帮助!
您的 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
class 的实例也应该尽可能短命,但现在大部分管道都被抽象掉了,因此您的调用代码可以如下所示:
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
以支持交易,and/or 如上图所示, 抽象出参数创建样板文件。
但想法仍然存在:您不会创建数据库连接并让它在全局范围内悬空,不知道某处的某些代码是否可能已将其设置为 Nothing
,或关闭它,或启动一个从未提交过的事务,或者天知道是什么。
- 创建
- 打开
- 执行
- 关闭
总是。范围尽可能小。那么你就不会有任何对象生命周期问题。
我使用 VBA 开发了一个小型 Excel 插件,它直接连接到数据库。我通过 DSN 建立了连接。打开插件并直接使用它时,它的效果非常好。但是,过了一会儿,与数据库的连接似乎超时了。更准确地说,完全有效的查询 return 在尝试打开记录集时出错。
我的代码是这样的:
'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
基本上,我检查连接是否打开。我的问题:所有这些检查 return TRUE,但连接根本没有打开。如果我连接,然后离开应用程序一段时间,然后返回它,以上所有内容都会 return 连接处于活动状态,但是当尝试使用新查询打开记录集时它将失败,大概是因为服务器关闭了连接或其他东西。我需要找到一种方法来检查连接是否真的能够打开记录集。
我可以ping 服务器什么的吗?如何检查数据库是否确实 return 是我查询的结果?有没有一种方法比仅向服务器发送测试查询并结合记录集上的错误处理具有更高的性能?我想那行得通,但我需要一个高性能的解决方案,而且我认为将简单连接检查的查询数量加倍并不是一个更好的解决方案...
感谢任何帮助!
您的 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
class 的实例也应该尽可能短命,但现在大部分管道都被抽象掉了,因此您的调用代码可以如下所示:
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
以支持交易,and/or 如上图所示, 抽象出参数创建样板文件。
但想法仍然存在:您不会创建数据库连接并让它在全局范围内悬空,不知道某处的某些代码是否可能已将其设置为 Nothing
,或关闭它,或启动一个从未提交过的事务,或者天知道是什么。
- 创建
- 打开
- 执行
- 关闭
总是。范围尽可能小。那么你就不会有任何对象生命周期问题。