Использование словаря сценариев для хранения объектов в MS Access, чтобы избежать циклических ссылок и позволить формам знать их владельцев объектов - PullRequest
0 голосов
/ 14 февраля 2020

Я создаю базу данных Access с классами, например, clsOrder, clsCustomer et c, которые управляют интерфейсом с помощью таблиц. Эти классы создают экземпляры форм при отображении своих данных. Я обнаружил, что, как только выполнение кода было в одной из этих форм, я не мог ссылаться на родительский объект, который его создал (так есть ли лучший способ сделать это? Будет частью моего вопроса).

Чтобы справиться с этим, я использую скрипт-словарь для хранения экземпляров классов с ключом, используя идентификатор класса и уникальный идентификатор класса (например, Order-3265). Затем я сохраняю ссылку на объект-владелец в самой форме.

Поэтому, когда объект создается и его идентификатор известен, он помещает указатель на себя в словаре и дает этот указатель на его форму (надеюсь, это достаточно ясно).

Это позволяет форме взаимодействовать с классом ее владельца.

Я использую другой класс clsManager для добавления элементов в Словарь, а также для поиска или удаления (с уничтожением).

Примеры классов - серьезно сокращены ..

clsManager:

Public WorkingObjects As New Scripting.Dictionary

Public Function AddWorkingObject(key As String, ObjectType As Object) As Boolean
    If Me.WorkingObjects.Exists(key) Then
        Me.WorkingObjects.Remove key
        Me.WorkingObjects.Add key, ObjectType
    Else
        Me.WorkingObjects.Add key, ObjectType
    End If
End Function

Public Function GetWorkingObject(key As String) As Object
    If Me.WorkingObjects.Exists(key) Then
        Set GetWorkingObject = Me.WorkingObjects(key)
    Else
        Set GetWorkingObject = Nothing
    End If
End Function

Public Function DestroyObject(obj As Object) As Boolean
Dim key As String
    If Not obj Is Nothing Then
        key = obj.DictionaryKey
        If Me.WorkingObjects.Exists(key) Then
            Me.WorkingObjects.Remove (key)
            Set obj = Nothing
            If obj Is Nothing Then
                Debug.Print key & " destroyed"
            Else
                Debug.Print obj.DictionaryKey & " NOT destroyed"
            End If
        End If
        Set obj = Nothing
    End If
End Function

clsQuote:

Option Compare Database
    Option Explicit    

'use a form using an instance of this class to control manipulation of Quote records
'Loading and saving set default values if a null value is detected    

Private Const scTABLE As String = "tblQuote"    

Private intID As Long 'unique identifier
Private intCustomerID As Long
Private intSiteID As Long
Private rsQuoteTotalValues As DAO.Recordset
Private oCustomer As clsCustomer
Const ObjectType = "Quote-"
Private oEditForm As Form_frmQuote    

Property Get EditForm() As Form_frmQuote
    Set EditForm = oEditForm
End Property    

Property Get ID() As Long
    ID = intID
End Property
Property Let ID(QuoteID As Long)
    intID = QuoteID
    Me.EditForm.ID = QuoteID
End Property    

Property Get Customer() As clsCustomer
    Set Customer = oCustomer
End Property    

Property Let CustomerID(ID As Long)
    intCustomerID = ID
    oCustomer.Load (ID)
    EditForm.SiteID.RowSource = oCustomer.AddressSQL
    EditForm.SiteID.Requery
    EditForm.ContactID.RowSource = oCustomer.ContactsSQL
    EditForm.ContactID.Requery
    EditForm.CustomerID = ID
End Property    

Property Get DictionaryKey() As String
    DictionaryKey = ObjectType & CStr(Me.ID)
End Property
'END PROPERTIES//////////////////////////////////    

Public Sub DisplayForm(Visibility As Boolean)
    With Me.EditForm
        .Visible = False
        .subFrmQuoteSectionsSummary.SourceObject = ""
        If Visibility = True Then
            ...some stuff...
            .Visible = True
        End If
    End With
End Sub    

Public Function Load(ID As Long) As Boolean
'On Error GoTo HandleError
Dim RS As DAO.Recordset
Dim sQry As String
    Load = False
    If Nz(ID, 0) <> 0 Then
        sQry = "SELECT * FROM " & scTABLE & " WHERE ([ID]=" & ID & ");"
        Set RS = Manager.DB().OpenRecordset(sQry, dbOpenForwardOnly)
            With RS
                If .RecordCount = 0 Then
                    MsgBox "Cannot find Quote with ID = " & ID, vbCritical
                    GoTo Done
                End If
                Me.ID = Nz(!ID, 0)
                Me.CustomerID = Nz(!CustomerID, 0)
                Manager.AddWorkingObject Me.DictionaryKey, Me
                Me.EditForm.SetOwnerObject (Me.DictionaryKey)
                .Close
            End With
        Set RS = Nothing
        Load = True
    End If
Done:
    Exit Function
HandleError:
    MsgBox "Error in Customer Load: " & vbCrLf & Err.Description, vbCritical
    Resume Done
End Function    

Private Sub Class_Initialize()
    Debug.Print "Quote class initialized"
    Set oCustomer = New clsCustomer
    Set oEditForm = New Form_frmQuote
    Me.ID = 0
    Set oQuoteTidier = New clsClassTidier
    Me.DisplayForm (False)
End Sub    

Private Sub Class_Terminate()
    Set oCustomer = Nothing
    Set oEditForm = Nothing
    Debug.Print "Quote class terminated"
End Sub    

Из формы редактирования:

Option Compare Database
Option Explicit    

'necessary for the object to have a reference to its owner in this manner to prevent circular reference
Private OwnerObject As clsQuote    

Public Function SetOwnerObject(OwnerKey As String) As Boolean
    SetOwnerObject = False
    Set OwnerObject = Manager.GetWorkingObject(OwnerKey)
    SetOwnerObject = True
End Function    

Private Sub cmdClose_Click()
    OwnerObject.EditForm.Visible = False
    Manager.DestroyObject OwnerObject
    DoCmd.Close acForm, Me.Name
End Sub    

Каждый класс бизнес-объектов (например, ClsOrder) имеет экземпляр editForm, который загружается и скрывается до тех пор, пока он не требуется, и до 3 наборов записей DAO, которые он сохраняет открытыми.

Я думаю, что все ссылки на взаимосвязанные бизнес-объекты являются указателями на объекты в словаре.

Моя проблема - ошибка 3035, превышающая системные ресурсы. Я проверил, что объекты уничтожаются, когда они не используются, но многократное открытие и закрытие объектов приводит меня к ошибке 3035.

Итак, вопрос в том, спрашиваю ли я Access о том, что он не может или лучше программирует исправить это?

1 Ответ

1 голос
/ 14 февраля 2020

Я вижу НУЛЕВЫЕ причины написать весь этот код. Почему бы не позволить форме обрабатывать все это? Помните, что каждая форма на самом деле является экземпляром класса. Вы даже можете запустить несколько копий одной формы, каждая со своим собственным кодом, собственными данными, и каждый экземпляр той же формы может работать на 100% независимо от других рабочих копий той же формы.

Если вы пытаетесь взглянуть на эту проблему и хотите получить объект класса для формы, просто используйте объект формы - это то, что он делает для вас!

Я вижу нулевую выгоду от писать весь этот код. Хотя. net имеет менеджер набора данных и систему (и теперь очень похожую структуру сущностей, это НАМНОГО сделано, поскольку. net не имеет привязанных к данным форм.

В Access каждая форма на самом деле объект класса. И это включает любой subli c sub или функцию для этой формы (так что функции становятся методами этой формы, а publi c vars становятся свойствами этой формы). В дополнение к связанной форме, имеющей события loadload эти события работают как действия против любого редактирования данных. Таким образом, в отличие от большинства систем, у вас есть событие «при изменении», до события обновления, после события обновления. Таким образом, просто приняв связанную форму, вы получите: Объект класса automati c создан для вас. У вас может быть несколько экземпляров этого класса и, следовательно, несколько экземпляров одной и той же формы, открытых одновременно. Вы получаете все те события данных, которые можно использовать для проверки ввода данных (или пользователь не может сохранить запись до тех пор, пока ваш критерий не будет достигнут. У вас есть полное использование всех столбцов данных, накануне n если элементы управления НЕ размещены на форме, связанной с этими столбцами. Таким образом, вы даже получаете разумный смысл для всех столбцов данных - это ваша карта.

Я не знаю, что здесь есть какая-то большая огромная круговая ссылка. Это все равно, что ткнуть пальцем в ногу, но потом пойти к врачу для какой-то огромной операции на открытом сердце. Таким образом, go для огромного массового кодирования и для того, чтобы потратить огромные суммы денег разработчиков на какую-то "редкую" проблему какой-то редкой и невидимой циклической ссылки - по сути, огромная дикая погоня за goose, которая будет Вы жевали огромное количество кода разработчика и время, когда вообще НИЧЕГО не требуется.

Я имею в виду, если, скажем, у вас открыто 3 экземпляра той же формы? Тогда как код узнает и ссылается на то, что такое форма? Ну, точно такой же подход c, используемый в типичном программировании ОО, может и должен быть использован здесь. Такой подход означает, что вы не жестко кодировать формы! имя или ссылки в коде когда-либо. Вы никогда не хотите этого делать.

Итак, если вы находитесь в подформе и вам нужно ссылаться на данные или элементы управления в родительской форме?

Вы можете сделать это:

strLastName = forms! FrmCustomer! LastName

Выше мы жестко закодировали имя формы. Вы не хотите этого делать.

В этой подчиненной форме правильный способ написания этого кода:

strLastName = me.Parent.form! LastName

Сейчас Обратите внимание, как вышесказанное ссылается на родительскую форму. Таким образом, этот код будет работать ДАЖЕ, если у нас одновременно будет 3 активных копии frmCustomer. Вы можете полностью ссылаться НИЧЕГО в форме по ее объекту «экземпляр». Так, в JavaScrip, или c#, вы часто видите «this.SomProperty» как ссылку на этот объект.

В доступе вы можете сделать то же самое и использовать «я». Или me.Parent.From для ссылки на родительскую форму. Таким образом, в качестве общего подхода, вы НИКОГДА не должны ссылаться на жесткие формы кода. Если вы воспользуетесь этим подходом, то все проблемы циклических ссылок будут не только устранены, но и тогда вы используете классический c и традиционный подход к объектному программированию и объектным ссылкам. Хотя Access не является полностью ОО, он, безусловно, следует многим концепциям ОО-проектирования, и то, как формы работают в Access, скорее всего, являются экземплярами объекта.

Попытка написать загруженный код кода, когда объектная модель форм уже существует в виде «единого» экземпляра объекта класса этой формы, не имеет смысла и не требуется, и путь, по которому вы идете, скорее всего, затруднит и уменьшит ваши возможности для разберитесь с экземпляром fantasti c той формы, которая у вас уже есть.

Как уже отмечалось, к форме уже прикреплены словари и столбцы, и Access EVEN генерирует элементы для вашего автомата c. В результате вы можете ссылаться на любой столбец таблицы, к которому привязана форма, с помощью

me.LastName me! LastName

В то время как два вышеуказанных формата разрешены, первый (me + точка) + имя столбца) на самом деле является членом класса форм. Вы обнаружите, что если вы используете код для установки источника данных форм, то часто эти элементы НЕ генерируются для вас, и, следовательно, вы должны использовать! (взрыв), чтобы ссылаться на столбцы из таблицы для этой формы.

Итак, я не получаю asp, пока вы пытаетесь выполнить весь этот дополнительный код, когда форма имеет все способности, которые вы запрашиваете в объекте класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...