ADO 记录集数据未显示在表单上
Posted
技术标签:
【中文标题】ADO 记录集数据未显示在表单上【英文标题】:ADO Recordset data not showing on form 【发布时间】:2016-12-01 13:21:51 【问题描述】:我在 MS Access 2010 上遇到了一个令人沮丧的问题,在这个阶段我将其归为错误。在尝试了所有可能的解决方法之后,我没有想法并依赖你。
上下文
具有 25k 行 VBA 和 >50 个表单的巨大 Ms Access 2010 应用程序。它有一个客户端服务器体系结构,其中包含一个编译好的前端和一个网络上的 Access 后端。它连接到 20 个不同的数据库 (Oracle/SQL Server/Sybase IQ)。
问题
有时当我将 ADODB 记录集分配给子表单时,其数据不会显示在绑定字段中。我到处都有#Name?
数据就在那里。我可以debug.print
它,我可以在 Watches 浏览器中看到它,我可以在使用代码循环记录集对象时读取或操作它。它只是没有出现在子窗体中。
它可以在几个月内完美运行,突然一个表单会在没有任何明显原因的情况下开始出现此问题(即使在我没有更改的表单上也可能发生)。当它发生时,它适用于所有用户,所以这在前端 accdb/accde 中确实有问题。
此问题与特定的 DBMS/驱动程序无关。 Oracle 或 Sybase 数据可能会发生这种情况。
我创建了自己的类来抽象与 ADO 连接和查询相关的所有内容,并且在任何地方都使用相同的技术。我已经有十分之几的表单基于它,并且大多数都可以完美运行。
我的应用程序的多个部分都存在这个问题,尤其是在包含大量子表单和代码的高度复杂的表单中。 在这个主窗体上,一些子窗体有问题,而其他子窗体没有。它们具有完全相同的参数。
代码
这就是我填充表单记录集的方式:
Set RST = Nothing
Set RST = New ADODB.Recordset
Set RST = Oracle_CON.QueryRS(SQL)
If Not RST Is Nothing Then
Set RST.ActiveConnection = Nothing
Set Form_the_form_name.Recordset = RST
End If
Oracle_CON.QueryRS(SQL)
调用的代码是
Public Function QueryRS(ByVal SQL As String, Optional strTitle As String) As ADODB.Recordset
Dim dbQuery As ADODB.Command
Dim Output As ADODB.Recordset
Dim dtTemp As Date
Dim strErrNumber As Long
Dim strErrDesc As String
Dim intSeconds As Long
Dim Param As Variant
If DBcon.state <> adStateOpen Then
Set QueryRS = Nothing
Else
DoCmd.Hourglass True
pLastRows = 0
pLastSQL = SQL
pLastError = ""
pLastSeconds = 0
Set dbQuery = New ADODB.Command
dbQuery.ActiveConnection = DBcon
dbQuery.CommandText = SQL
dbQuery.CommandTimeout = pTimeOut
Set Output = New ADODB.Recordset
LogIt SQL, strTitle
dtTemp = Now
On Error GoTo Query_Error
With Output
.LockType = adLockPessimistic
.CursorType = adUseClient
.CursorLocation = adUseClient
.Open dbQuery
End With
intSeconds = DateDiff("s", dtTemp, Now)
If Output.EOF Then
LogIt "-- " & Format(Now, "hh:nn:ss") & " | Executed in " & intSeconds & " second" & IIf(intSeconds = 1, "", "s") & " | Now rows returned."
Set QueryRS = Nothing
Else
Output.MoveLast
pLastRows = Output.RecordCount
LogIt "-- " & Format(Now, "hh:nn:ss") & " | Executed in " & intSeconds & " second" & IIf(intSeconds = 1, "", "s") & " | " & Output.RecordCount & " row" & IIf(Output.RecordCount = 1, "", "s") & " returned."
Output.MoveFirst
Set QueryRS = Output
End If
End If
Exit_Sub:
pLastSeconds = intSeconds
Set Output = Nothing
Set Parameter = Nothing
Set dbQuery = Nothing
DoCmd.Hourglass False
Exit Function
Query_Error:
intSeconds = DateDiff("s", dtTemp, Now)
strErrNumber = Err.Number
strErrDesc = Err.DESCRIPTION
pLastError = strErrDesc
MsgBox strErrDesc, vbCritical, "Error " & pDSN
LogIt strErrDesc, , "ERROR"
Set QueryRS = Nothing
Resume Exit_Sub
Resume
End Function
到目前为止我尝试过的事情
对于记录集,我尝试了所有可能的变化
.LockType = adLockPessimistic
.CursorType = adUseClient
.CursorLocation = adUseClient
处理记录集的子表单都有Snapshot
记录集类型,如果我尝试dynaset
,问题仍然存在。
数据输入、添加、删除、编辑都被禁用。它是纯只读的。
我有使用RST.ActiveConnection = Nothing
断开记录集的习惯,以便之后可以对其进行操作,但这也不会影响问题。
在SELECT
子句中只有一个字段且子表单上只有一个字段绑定到它的非常简单的查询中会发生这种情况。
在新的 accdb 中重新导入所有对象也不能解决问题。
random_answer_guy 提出的解决方案乍一看是有效的,它证实了错误假设。不幸的是,在主表单发生一些(完全不相关的)更改后,我的问题再次出现。我回来了,有 4 或 5 个子表单不显示数据,并且在全部或部分子表单上添加/删除 Load 事件不再有任何区别
如果您想了解有关此问题有多奇怪的更多信息,建议您阅读我对 random_answer_guy 答案的评论。
总结
非常令人沮丧的是,我可以有两种不同的表单,它们具有完全相同的属性和相同的字段,相同的数据库上的相同 SQL 指令,相同的记录集管理代码:一个显示数据,另一个不显示!
当问题发生时,我别无选择,只能删除所有被操作的对象并从旧版本重新导入它们,或者从头开始重新创建它们。
如果这不是错误,我仍在寻找合适的词来限定它。
有没有人遇到过这个问题并提出解释和/或解决方法?
【问题讨论】:
这些表单是否有 Form_Load 事件?我知道奇怪的问题,但有一次我遇到了几乎相同的问题,只需将 Form_Load 事件添加到 Form 即可修复它。事件中没有代码只是一个空白的 Form_Load... 通常在两种情况下显示混乱。 #1,你有空位的列,#2,你没有行版本列。我会调查这两个。您还希望确保在所有情况下都有一个 PK 列。 @ThomasG,我注意到您说前端已编译。Debug -->> Compile VBA Project
确实会导致一些奇怪的行为。是的,它会检查语法,但也会创建大量垃圾代码,这些代码会导致您的应用程序行为异常。查看this answer 了解更多信息。我建议将你所有的模块、表单、对象等导出到一个干净的访问文件中,并确保不要编译。
@JosephWood 即使在 accdb/未编译版本上也会发生
同时。我优雅地绕过了这个问题:我在抽象 ADO 的类中添加了一个 .QueryDAORS
方法。它在输入中也需要一个 SQL 查询并返回一个记录集,但是通过创建一个临时 QUEeryDef 来使用 DAO。我只需将我的临时记录集重新声明为 DAO 而不是 ADODB 并更改调用的方法。那很快。我注意到大多数有 ADO 显示问题的表单仍然拒绝显示数据。在使用 DAO 方法之前,我必须从头开始重新创建表单,现在它们都可以完美运行。我坚持这一点。问题肯定在表单级别。
【参考方案1】:
我之前也遇到过同样的问题,只需添加一个空白Form_Load
事件即可解决问题。 Form_Load
不需要任何代码,它只需要存在即可。
【讨论】:
更奇怪的是:在同一个表单上,我有 4 个子表单存在这个问题。我在第一个事件中添加了一个 Load 事件。重新编译,所有子表单都神奇地工作。我告诉自己,“好吧,让我们聪明点,为所有子表单添加一个 Load 事件”,我继续,重新编译,没有人再次工作!然后我删除了所有的 Load 事件,但第一个,重新编译,它们都在工作。我只是不知道这是什么鬼东西。 MS Access 绝对不是一门精确的科学。 虽然,这救了我几天...问题又回来了 :-) 我尝试了各种形式的 OnLoad/OnOpen 事件的所有可能组合,但没有任何效果,我又回到了问题上root 有 4 或 5 个子窗体不显示数据。因此,我显然保留对您的提案的支持,但删除已回答的标记,因为我希望这个问题存在并且我愿意为此设置赏金。【参考方案2】:所以在这个阶段没有人可以对主要问题给出明确的答案:
为什么会出现这个错误?
与此同时,我通过将遇到错误的子表单的方法从 ADO 更改为 DAO,“优雅地”绕过了这个问题。
我在我的 ADO 抽象类中创建了一个新方法,它实际上使用 DAO 返回一个记录集(不合逻辑,但是嘿......)。
我将数据传递给表单的代码变为:
Set RST = Nothing
Set RST = Oracle_CON.QueryDAORS(SQL)
If Not RST Is Nothing Then
Set Form_the_form_name.Recordset = RST
End If
这是QueryDAORS
的方法:
Public Function QueryDAORS(ByVal SQL As String, Optional strTitle As String) As DAO.Recordset
Dim RS As DAO.Recordset
Dim dtTemp As Date
Dim strErrNumber As Long
Dim strErrDesc As String
Dim intSeconds As Long
Dim Param As Variant
On Error GoTo Query_Error
dtTemp = Now
If DBcon.state <> adStateOpen Then
Set QueryDAORS = Nothing
Else
DoCmd.Hourglass True
Set pQDEF = CurrentDb.CreateQueryDef("")
pQDEF.Connect = pPassThroughString
pQDEF.ODBCTimeout = pTimeOut
pQDEF.SQL = SQL
pLastRows = 0
pLastSQL = SQL
pLastError = ""
pLastSeconds = 0
LogIt SQL, strTitle, , True
Set RS = pQDEF.OpenRecordset(dbOpenSnapshot)
intSeconds = DateDiff("s", dtTemp, Now)
If RS.EOF Then
LogIt "-- " & Format(Now, "hh:nn:ss") & " | Executed in " & intSeconds & " second" & IIf(intSeconds = 1, "", "s") & " | Now rows returned."
Set QueryDAORS = Nothing
Else
RS.MoveLast
pLastRows = RS.RecordCount
LogIt "-- " & Format(Now, "hh:nn:ss") & " | Executed in " & intSeconds & " second" & IIf(intSeconds = 1, "", "s") & " | " & RS.RecordCount & " row" & IIf(RS.RecordCount = 1, "", "s") & " returned."
RS.MoveFirst
Set QueryDAORS = RS
End If
End If
Exit_Sub:
pLastSeconds = intSeconds
Set RS = Nothing
DoCmd.Hourglass False
Exit Function
Query_Error:
intSeconds = DateDiff("s", dtTemp, Now)
strErrNumber = Err.Number
strErrDesc = Err.DESCRIPTION
pLastError = strErrDesc
MsgBox strErrDesc, vbCritical, "Error " & pDSN
LogIt strErrDesc, , "ERROR"
Set QueryDAORS = Nothing
Resume Exit_Sub
Resume
End Function
属性pPassThroughString
是用另一个方法定义的,使用我在课堂上已经拥有的属性,因为它们是打开到数据库的 ADO 连接所必需的:
Private Function pPassThroughString() As String
Select Case pRDBMS
Case "Oracle"
pPassThroughString = "ODBC;DSN=" & pDSN & ";UID=" & pUsername & ";Pwd=" & XorC(pXPassword, CYPHER_KEY)
Case "MS SQL"
pPassThroughString = "ODBC;DSN=" & pDSN & ";DATABASE=" & pDBname & ";Trusted_Connection=Yes"
Case "Sybase"
pPassThroughString = "ODBC;DSN=" & pDSN & ";"
Case Else
MsgBox "RDBMS empty ! ", vbExclamation
LogIt "RDBMS empty ! ", , "ERROR"
End Select
End Function
因此,只需将分配给表单的记录集从 ADODB.Recordset
更改为 DAO.recordset
,并将调用的方法从 .OpenRS
调整为 .OpenDAORS
,即可快速解决问题。
唯一的缺点是使用 DAO 我不能再使用它来断开记录集:
Set RST.ActiveConnection = Nothing
不过,我还是希望得到解释并修复:(
【讨论】:
以上是关于ADO 记录集数据未显示在表单上的主要内容,如果未能解决你的问题,请参考以下文章
使用类模块将可编辑的 ADO 记录集返回到 MS Access 表单
将 DAO 记录集转换为断开连接的 ADO 记录集 dbDecimal 问题
使用 VBA 在 Access 2010 中的表单上显示记录集