【问题标题】:ACE DAO recordsets returning zero fields under vb.netACE DAO 记录集在 vb.net 下返回零字段
【发布时间】:2021-04-14 21:40:55
【问题描述】:

我正在转换一些在 VBA 中开发的遗留代码,并使用 DAO 来执行数据库查询、更新等。我需要转换一些非 GUI 功能,以便它可以在无人看管的情况下运行而无需运行 Access。所以我将 VBA 代码移植到 VB.net,使用 Access 数据库引擎对象库 (ACE) 来提供 DAO 接口。

它在一定程度上起作用:在创建和打开(然后关闭)记录集一定(未知)次数后,它开始返回具有正确记录数的记录集,但其字段集合中没有成员。因此,尝试使用 Fields("Name").Value 获取字段的值会引发“在此集合中找不到项目”异常。

这不是特定查询的问题:在某些情况下,具有完全相同参数的完全相同的查询在程序执行的早期工作得很好(基础数据没有变化)。如果我重新排列程序各部分的执行顺序,那么我会在程序的不同部分使用不同的查询得到相同类型的错误(即返回空字段集合的记录集)。

所以看起来 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 行开始抛出“集合中未找到项目”异常,因为字段集合为空。

【问题讨论】:

  • 愿意分享一些代码吗?

标签: .net ms-access dao


【解决方案1】:

好的,这是 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, FirstName, LastName from tblhotels",
                        New OleDbConnection(My.Settings.TESTAce))
        cmdSQL.Connection.Open()

        MyTable.Load(cmdSQL.ExecuteReader)

    End Using

所以请注意如何在代码中换出“提供者”。但是,数据表、数据集和数据行等基础对象在所有情况下都是相同的。

其实是因为上面的?我实际上会考虑使用 ODBC 提供程序,从那时起您可以换出连接字符串,并说开始使用 SQL 服务器 - 并且只需要很少的代码更改。而且我和微软都不再建议对 sql server 使用 oleDB 提供程序。但他们确实建议 sqprovider(sql server 的本地 .net 提供程序)或 ODBC 仍然受到广泛支持和建议使用的提供程序。

但他们也建议您避免使用 oleDB 来对抗 sql server。事实上,这些天使用 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 对象(数据表、数据行和 MORE稀有数据集)。

所以,我构建了一个 MyRst 和一个 MyRstEdit 函数 - 它们返回一个 .net 数据表。结果,我实际上发现很多例程的代码比以前少了一些,或者至少代码遵循与 VBA 代码相同的逻辑和流程。

【讨论】:

  • 感谢 Albert,这很有帮助。当您说“DAO 不受管理并且会导致内存泄漏”时,我认为您已经一针见血了。
  • 不幸的是,遗留代码实际上有数百个 DAO 调用,因此我通过 Access 数据库引擎对象库 (ACE) 使用 DAO。似乎我得到的错误是内存泄漏表现出来 - 这个解释符合我上面描述的行为。
  • 查看我的编辑 + cmets。您可能在处理 dao 对象时更加小心(使用块或 object.Dispose() 会有很大帮助)。但是,既然您必须在 vb.net 中重新分解该代码?然后阅读 + 注意我在上面的帖子中添加的后续 cmets - 我发现这种方法更好,因为那时我没有在 .net 代码中引用 DAO 对象库。
  • 谢谢。看起来我将不得不走重构路线,因为 DAO 对象没有实现 System.IDisposable,因此尝试对它们应用 Using 块或 .Dispose() 会导致编译错误。
  • 我没有意识到这一点。也许设置对象没有任何帮助(但我不建议这样做,因为我不知道这个想法是否会/会有所帮助)。如前所述,我将一些真正的 harry ball VBA 代码剪切 + 粘贴到 vb.net 中 - 并使用了 .net 对象,如数据表/数据行。重构非常顺利。如前所述,我添加了一个 nz() 例程和一个 MyRst() 例程——这极大地促进了重构——因为我没有重新创建连接对象,而只是使用 MyRst() 函数来替换 dao。打开ReocrdSet。使用数据表等的 2-3 个辅助例程会创造奇迹
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多