我使用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服务器吗?如何检查数据库是否实际将结果返回给我的查询?是否有一种方法具有比仅仅将测试查询发送到服务器以及记录集上的错误处理更高的性能?我想这可行,但我需要一个高性能的解决方案,我不认为将简单连接检查的查询数量增加一倍是一个很好的解决方案......
感谢任何帮助!
答案 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
,或者关闭它,或者启动了从未犯过的交易,或者上帝知道什么。
始终。尽可能紧凑。然后你就不会有任何对象生命周期问题。