问题描述
我正在转换一些在访问中使用 VBA 开发的遗留代码,并使用 DAO 执行数据库查询、更新等。我需要转换一些非 GUI 功能,以便它可以在无人看管的情况下运行,而无需运行 Access。所以我已经将 VBA 代码移植到 VB.net,使用 Access 数据库引擎对象库 (ACE) 提供 DAO 接口。
它在一定程度上有效:在创建和打开(然后关闭)记录集一定(未知)次数后,它开始返回具有正确记录数的记录集,但其 Fields 集合中没有成员。因此,尝试使用 Fields("Name").Value
获取字段的值会引发“在此集合中找不到项目”异常。
这不是特定查询的问题:在某些情况下,具有完全相同参数的完全相同的查询在程序执行的早期运行良好(不更改基础数据)。如果我重新排列程序部分的执行顺序,那么我会在程序的不同部分使用不同的查询得到相同类型的错误(即返回的记录集为空 Fields 集合)。
所以看起来 DAO 库中存在某种错误,在打开和关闭一定数量的记录集后它会失败。但它只发生在 .Net 下,而不是在 VBA 下。
有人遇到过这个问题吗?有什么解决方法吗?
谢谢!
更新:有人要求我发布一些代码。正如我上面所描述的,它不会发生在代码中的特定点 - 我可以根据执行流程在不同的地方发生。这是它发生的地方之一的示例。这是一个从选项表中检索选项值的简单函数:
Public Function GetSolverOption(ParameterName As String) As Object
'
' o Gets the value of a solver option.
Dim dbsjet As Microsoft.Office.Interop.Access.Dao.Database
Dim qdfControl As Microsoft.Office.Interop.Access.Dao.QueryDef
Dim rstControl As Microsoft.Office.Interop.Access.Dao.Recordset
dbsjet = CurrentDB
qdfControl = dbsjet.QueryDefs("Q_Solver Options")
qdfControl.Parameters("Q_Parameter").Value = ParameterName
rstControl = qdfControl.OpenRecordset
GetSolverOption = rstControl.Fields("Value").Value
rstControl.Close()
End Function
此函数被多次调用而没有任何问题,但随后 GetSolverOption = rstControl.Fields("Value").Value
行开始抛出“Item not found in collection”异常,因为 Fields 集合为空。
解决方法
好的,这是偶然的asp.net吗?还是只是台式机?
在asp.net中?有错误。并且您没有提及您使用的是 .net ODBC 提供程序还是 oleDB 提供程序。您可以随意使用任何一种。
我不知道你是说每次打开一个新连接,还是在启动时创建一个全局连接对象并一遍又一遍地使用它?
我认为你不应该在 .net 中引用或使用 DAO 库(我不知道或认为你是)。如果您 - 不要 - DAO 是不受管理的,会导致内存泄漏。
那么,读取一些数据说成一个表?你可以用这个:
Dim MyTable As New DataTable
Using cmdSQL As New OdbcCommand("SELECT ID,FirstName,LastName from tblhotels",New OdbcConnection(My.Settings.TESTAce))
cmdSQL.Connection.Open()
MyTable.Load(cmdSQL.ExecuteReader)
End Using
现在在上面,我们确实创建了一个连接,然后打开它,然后由于代码的“使用块”,它应该被处理和丢弃。但是,如果您使用的是 asp.net,则在打开大约 60 次后 - 它会爆炸。而且你必须在上面添加一个 dispose 命令 - 这是由于 asp.net 的连接池。
所以要解决这个问题,然后:例如:
MyTable.Load(cmdSQL.ExecuteReader)
cmdSQL.Dispose
End Using
在上面?我使用了 ODBC .net 提供程序。如果您使用 oleDB 提供程序(并且您可以),那么上面确实是“相同的”,但是使用 oleDB 提供程序,您会得到:
Dim MyTable As New DataTable
Using cmdSQL As New OleDbCommand("SELECT ID,New OleDbConnection(My.Settings.TESTAce))
cmdSQL.Connection.Open()
MyTable.Load(cmdSQL.ExecuteReader)
End Using
所以请注意如何在代码中换出“提供者”。但是,数据表、数据集和数据行等基础对象在所有情况下都是相同的。
其实是因为以上?我实际上会考虑使用 ODBC 提供程序,从那时起您可以交换连接字符串,并说开始使用 SQL 服务器 - 并且代码更改很少。我和 Microsoft 都不建议再针对 sql server 使用 oleDB 提供程序。但他们确实建议 sqprovider(sql server 的本机 .net 提供程序)或 ODBC 仍然得到广泛支持和建议使用的提供程序。
但他们也建议您避免针对 sql server 使用 oleDB。事实上,这些天使用 oleDB 的唯一真正令人信服的理由是什么?如果您仍在使用 Access(ACE/JET 数据引擎),那当然是为什么。
不要太担心上述问题。但是你没有注意到也没有提到你是否每次都使用一个新的连接对象,或者将整个混乱包装在一个 using 块中(它应该为你自动处理连接对象和命令对象。
请注意,虽然我在上面使用了“新连接对象”,但我可以使用预先创建的 oleDB(或 odbc)连接对象,该对象是/是预先创建的 - sql 命令对象接受字符串,或实际上是一个连接对象 - 您的选择。
那么,第一个真正的问题是:您是按照上述方式处理每次使用的连接对象,还是重新使用一个给定的连接对象?我会考虑为整个应用程序一次性创建一个连接对象 - 这可以/将消除连接对象的重新创建和处理。
然而,你肯定不想在没有 using 块的情况下一遍又一遍地创建新的连接对象——或者至少在你的代码中有一个 connection.dispose 。这可以解释随着时间的推移 - 您的代码中的事情开始向南。
编辑: 好的,鉴于我们已经说了一堆代码 - VBA,我们想在 vb.net 中转换 + 使用?
我以前这样做过,转化率还不错。
所以,说拿这个代码片段:
VBA:
Dim MyDB As Database
Dim myRST As Recordset
Set MyDB = CurrentDb()
dim strSQL as String
strSQl = "SELECT * from ProjectComponentHeader where ID = MyForm.ProjectCompoentID
Set myRST = MyDB.OpenRecordset(strSQL,dbOpenDynaset,dbSeeChanges)
myRST.Edit
myRST!StPrepress = "WAIT - APPROVAL"
If Nz(myRST!FirstProofSentDate,0) = 0 Then
myRST!FirstProofSentDate = Now()
End If
myRST!ProofSentDate = Now()
myRST.Update
那么,要重新考虑上面的因素,比如说使用 .net 中的 oleDB 提供程序?
上面会变成这样:
Dim da As OleDbDataAdapter
Dim rst As DataRow
Dim strSQL As String
strSQL = "SELECT * from ProjectComponentHeader where ID = " & ProjectCompoentID
rst = MyrstEdit(strSQL).Rows(0),"",da)
rst("StPrepress") = "WAIT - APPROVAL"
If Nz(rst("FirstProofSentDate"),0) = 0 Then
rst("FirstProofSentDate") = Now()
End If
rst("ProofSentDate") = Now()
da.Update(Myrst)
因此,我构建了一个名为 MyRst 和 MyRstEdit 的例程。他们只是返回一个数据表。
我构建了一个 nz() 函数。
所以,现在,我“更多”地重构代码。我真的不必重新编写现有的代码逻辑 - 只是重新考虑使用 .net 对象。我用一些相当混乱的 VBA 和一些相当长且复杂的例程完成了上述工作。
我的意思是,甚至拉入 VBA 代码,并使用 DAO 对象引用?您仍然必须“检查”代码并重构为 vb.net 代码。正如您所注意到的,这个过程进行得非常快——而且工作量并不大。毫无疑问,转换需要额外的工作——但转换这些例程的工作量并不大——而且不会比你使用 DAO 对象而不是 .net 对象(数据表、数据行和更多稀有数据集)。
因此,我构建了一个 MyRst 和一个 MyRstEdit 函数 - 它们返回一个 .net 数据表。结果,我实际上发现很多例程的代码比以前少了一点,或者至少代码遵循与 VBA 代码相同的逻辑和流程。