Данные ADO Recordset, не отображаемые по форме

У меня есть разочаровывающая проблема в MS Access 2010, которую я бы на этом этапе квалифицировал как ошибку. И, пробовав все возможные обходные пути, я выхожу из идей и полагаюсь на вас.


Контекст

Приложение Huge Ms Access 2010 с 25-килограммовыми линиями VBA и > 50 форм. Он имеет архитектуру клиентского сервера с объединенным интерфейсом и бэкендом доступа в сети. Он подключается к двум различным базам данных (Oracle/SQL Server/Sybase IQ).


Проблема

Иногда, когда я назначаю набор записей ADODB для подформы, его данные не отображаются в связанных полях.20 >

Данные есть. Я могу debug.print его, я вижу его в браузере Watches, я могу читать или манипулировать им, когда вы зацикливаете на объекте recordset с кодом. Он просто не отображается в подчиненной форме.

Он может работать безупречно в течение нескольких месяцев, и вдруг одна форма начнет иметь эту проблему без какой-либо видимой причины (это может произойти даже в тех формах, которые я не изменил). Когда это происходит, это делается для всех пользователей, так что это действительно что-то не так в интерфейсе accdb/accde.

Проблема не связана с конкретной СУБД/драйвером. Это может произойти с данными 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 recordsettype, проблема остается, если я пытаюсь dynaset. Dataentry, дополнение, удаление, изменения отключены. Это чистое чтение.

У меня есть привычка отключать записи с помощью RST.ActiveConnection = Nothing, поэтому я могу манипулировать ими впоследствии, но это также не влияет на проблему.

Это может происходить с очень простыми запросами только с одним полем в предложении SELECT и только с одним полем, привязанным к нему на подформе.

Реимпорт всех объектов в свежем accdb также не решает проблему.

Решение, предложенное random_answer_guy, с первого взгляда работало, что подтвердило гипотезу об ошибке. К сожалению, мои проблемы возникли после некоторых (тотально несвязанных) изменений в основной форме. Я вернулся с 4 или 5 подформами, не отображающими данные, и добавление/удаление события загрузки на всех или части из них больше не имеет никакого значения

Если вам нужна дополнительная информация о том, насколько странно эта проблема, я советую вам прочитать мой комментарий к отвечу random_answer_guy.


В заключение

Чрезвычайно разочаровывает то, что у меня могут быть две разные формы с точно такими же свойствами и одинаковыми полями, одна и та же инструкция SQL над одним и тем же БД, один и тот же код управления наборами записей: один показывает данные, а другой нет!

Когда проблема возникает, у меня нет другого выбора, кроме как удалить все объекты, которые будут обрабатываться и реимпортироваться из старой версии, или воссоздать их с нуля.

Если это не ошибка, я все еще ищу подходящее слово, чтобы квалифицировать ее.

Кто-нибудь когда-либо сталкивался с этой проблемой и объяснил и/или обходное решение, чтобы предложить?

Ответ 1

У меня была такая же проблема раньше, и просто добавление пустого события Form_Load разрешило проблему. Никакой код не должен быть с Form_Load, он просто должен присутствовать.

Ответ 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

Тем не менее, я бы предпочел получить объяснение и исправить: (