Neuigkeiten:

Wenn ihr euch für eine gute Antwort bedanken möchtet, im entsprechenden Posting einfach den Knopf "sag Danke" drücken!

Mobiles Hauptmenü

Schreibkonflikt bei Speichern von Daten via recordset

Begonnen von maddhin, August 26, 2020, 04:09:53

⏪ vorheriges - nächstes ⏩

maddhin

Hallo,

mal eine ganz konkrete Frage:

Auf meinem Firmen-Stammdaten-Formular habe ich einen Button, der einen Ordner auf der Festplatte anlegt bzw. wenn vorhanden diesen umbenennt bzw. verschiebt wenn sich der Pfad geändert hat.

Hierzu habe ich zwei Felder, ein Feld nimmt den individuellen Teil des Pfades a la \Hauptkategorie\Unterkategory\Firmename_gekürzt. Das andere Feld speichert den vollen Pfad (d.h. individueller Pfad plus den User-spezifischem Systempfad wie C:\meineDB\ und DB-spezifischem Ordernamen für die Firmendaten wie \Companies\). D.h. der volle Pfad ist dann Vom-User-gewähltes-Verzeichnis\DB-Firmenordner\Hauptkategorie\Unterkategory\Firmename_gekürzt.

Diese beiden Pfade speichere ich via recordset rs.edit / rs.update. Hier kommt es aber (oft) zu einem Schreibkonflikt wo Access fragt welche Daten behalten werden sollen. Nicht gut.
Wenn ich die Daten direkt zurück ins Formular gebe, gibt es keine Probleme.

Sie dürfen in diesem Board keine Dateianhänge sehen.

Ich würde gerne an der Lösung mit dem recordset festhalten, weil ich dann die Funktion auch von woanders anstoßen kann und beispielsweise bei Änderung des vom User gewählten Verzeichnisses alle Daten verschieben kann. Das klappt nicht, wenn ich die Daten auf das Formular zurückgebe...

Anbei mal die Funktion. Ist noch work-in-progress und, sorry, entsprechend wenig elegant (Holzhammer!).

'---------------------------------------------------------------------------------------
' Create company folder function incl update function
'---------------------------------------------------------------------------------------
Function CreateCompanyFolder(strIDCorp As String, strCorpName As String, frm As Form)
    Dim FSO As New FileSystemObject
    Dim strIDMainCat As String
    Dim strIDCat As String 'Category ID
    Dim RootPath As String 'this is the windows root path where all files are
    Dim CompanyRootFolder As String 'this is the sub-folder where all company folders are
    Dim MainCategoryFolder As String 'main category folder
    Dim CategoryFolder As String 'category folder
    Dim CompanyFolder As String
    Dim CorpLinkFolder As String 'this is the path of the variable company folder
    Dim CorpLinkFolderFullPath As String 'this is the full path of the company folder
    Dim db As Database
    Dim rs As Recordset
     
    Set db = CurrentDb
   
'RootPath: get root path from settings_user
    Set rs = db.OpenRecordset("tblSettingsUser", Type:=RecordsetTypeEnum.dbOpenDynaset)
   
    With rs
    .FindFirst "ID_Setting_User = 1" 'this needs to be controlled by login user!!
    End With
   
    If rs.Fields("root_path") = 0 Then
        RootPath = CurrentProject.path
    Else
        RootPath = rs.Fields("root_path")
    End If
   
'CompanyRootFolder: get Company folder from settings_DB
    Set rs = db.OpenRecordset("tblSettingsDB", Type:=RecordsetTypeEnum.dbOpenDynaset)
   
    With rs
    .FindFirst "ID_Setting_DB = 1"
    End With
   
    CompanyRootFolder = rs.Fields("Folder_Data_Company")

'CategoryFolder: get category ID and define category folder
    strIDCat = Nz(DLookup("[ID_Cat]", "qrysearchcompanylist", "[ID_Corp] = " & strIDCorp), "0")
    'MsgBox ("Category ID: " & strIDCat)

    If strIDCat = 0 Then
        MsgBox ("Company ID: " & strIDCorp & vbNewLine & vbNewLine & "Please assign category to company and retry!")
        Exit Function
    Else
        CategoryFolder = DLookup("Cat_FolderName", "tblcategory", "ID_Cat = " & strIDCat) & " (" & strIDCat & ")"
    End If

'MainCategoryFolder:
    strIDMainCat = Nz(DLookup("[ID_Cat_parent]", "tblcategory", "[ID_Cat] = " & strIDCat), "0")
    MainCategoryFolder = DLookup("Cat_FolderName", "tblcategory", "ID_Cat = " & strIDMainCat) & " (" & strIDMainCat & ")"

'CompanyFolder: get company folder name
    CompanyFolder = CompanyFolderName(strIDCorp, strCorpName)
   
'set full path and create folder
    'MsgBox (RootPath & "\" & CompanyRootFolder & "\" & CategoryFolder & "\" & CompanyFolder)
    CorpLinkFolder = MainCategoryFolder & "\" & CategoryFolder & "\" & CompanyFolder
    CorpLinkFolderFullPath = RootPath & "\" & CompanyRootFolder & "\" & CorpLinkFolder
   
    'get current folder name
    Set rs = db.OpenRecordset("tbl_company", dbOpenDynaset)
   
    rs.FindLast "ID_Corp = " & strIDCorp & ""
       
    If IsNull(rs!Corp_Link_Folder) Then 'if no company folder exists
        'MsgBox "there is nothing in the link folder, will create new folder"
        CreateFolderRecursive (CorpLinkFolderFullPath)
'        frm.Corp_Link_Folder = CorpLinkFolder
'        frm.Corp_Link_Folder_FullPath = CorpLinkFolderFullPath
        rs.Edit
            rs!Corp_Link_Folder = CorpLinkFolder
            rs!Corp_Link_Folder_FullPath = CorpLinkFolderFullPath
        rs.update
   
'    ElseIf CorpLinkFolderFullPath = frm.Corp_Link_Folder_FullPath Then 'if no change
'    'ElseIf CorpLinkFolderFullPath = RootPath & "\" & CompanyRootFolder & rs!Corp_Link_Folder Then 'if no change
'        MsgBox "no change to folder!"
'        If IsNull(rs!Corp_Link_Folder_FullPath) Then
'            frm.Corp_Link_Folder_FullPath = CorpLinkFolderFullPath
''            rs.Edit
''                rs!Corp_Link_Folder_FullPath = CorpLinkFolderFullPath
''            rs.update
'            GoTo Ende
'        End If
    Else
        'MsgBox "A Folder exists already, will change the folder name!"
        '1. create new folder
        If FSO.FolderExists(CorpLinkFolderFullPath) Then
            MsgBox "The Folder you want to create already exists! There seems to be some file/folder mess! Please check, manually erase target folder and retry!"
            frm.Corp_Link_Folder = CorpLinkFolder
            frm.Corp_Link_Folder_FullPath = CorpLinkFolderFullPath
            GoTo Ende
        Else
            CreateFolderRecursive (CorpLinkFolderFullPath)
        End If
       
        '2. move all files from old folder to new folder
        Set FSO = CreateObject("Scripting.FileSystemObject")
'        If FSO.FolderExists(FromPath) = False Then
'            MsgBox FromPath & " doesn't exist"
'        Exit Sub
'        End If
        If IsNull(rs!Corp_Link_Folder_FullPath) Then
            GoTo SkipCopy
        End If
        If FSO.FolderExists(rs!Corp_Link_Folder_FullPath) Then
            FSO.CopyFolder Source:=rs!Corp_Link_Folder_FullPath, Destination:=CorpLinkFolderFullPath
'        If Len(Dir(rs!Corp_Link_Folder_FullPath & "\*.*")) > 0 Then
'            FSO.MoveFile rs!Corp_Link_Folder_FullPath & "\*.*", CorpLinkFolderFullPath
'            FSO.MoveFolder rs!Corp_Link_Folder_FullPath & "\*", CorpLinkFolderFullPath
'        End If
       
        '3. erase old folder
            FSO.DeleteFolder rs!Corp_Link_Folder_FullPath
        End If
        '4. write new location
SkipCopy:
'        frm.Corp_Link_Folder = CorpLinkFolder
'        frm.Corp_Link_Folder_FullPath = CorpLinkFolderFullPath
        rs.Edit
            rs!Corp_Link_Folder = CorpLinkFolder
            rs!Corp_Link_Folder_FullPath = CorpLinkFolderFullPath
        rs.update
    End If
   
Ende:
    rs.Close
    db.Close
    Set rs = Nothing
    Set db = Nothing

End Function

Beaker s.a.

Hallo Martin,
Schuss ins Blaue; - speichere den DS im Formular explizit
bevor du die Function aufrufst
If Me.Dirty Then Me.Dirty = False
gruss ekkehard
Alles, was geschieht, geschieht. - Alles, was während seines Geschehens etwas anderes geschehen lässt, lässt etwas anderes geschehen. - Alles, was sich selbst im Zuge seines Geschehens erneut geschehen lässt, geschieht erneut. - Allerdings tut es das nicht unbedingt in chronologischer Reihenfolge.
(Douglas Adams, Mostly Harmless)

ebs17

ZitatSchreibkonflikt wo Access fragt welche Daten behalten werden sollen
Kann es sein, dass Du in einem gebundenen Formular hantierst (editierst) und zusätzlich per Recordset schreiben willst? So stellt man sich selber ein Bein.

Zusätzlich: FindFirst und besonders FindLast sind nicht wirklich überzeugend:
- Prinzipiell sollte man immer einkalkulieren (prüfen), dass nichts gefunden wird. Dann ist der Datenzeiger sonstwo. => NoMatch
- Statt im Recordset zu suchen ist es effizienter, das Recordset gleich gefiltert zu öffnen.

Recordsets nicht schließen ist ganz schlechter Stil und kann einem "ganz überraschend" mal gewaltig auf die Füße fallen.

Das andere habe ich mir nicht angesehen, bei der Sprungmarke habe ich dann rasch abgebrochen.
Mit freundlichem Glück Auf!

Eberhard

maddhin

Zitat von: ebs17 am August 28, 2020, 17:05:36Kann es sein, dass Du in einem gebundenen Formular hantierst (editierst) und zusätzlich per Recordset schreiben willst? So stellt man sich selber ein Bein.
Ja,... Wie gesagt, mit Recordsets kenne ich mich noch nicht aus - daher solche Fragen :)

Macht es einen Unterschied, wenn ich statt über das Recordset die Daten via SQL UPDATE aktualisiere?

Generell höre ich aus der Antwort heraus, mit ungebundenen (Feldern auf) Formularen zu arbeiten und die Daten via Recordset zu laden. Ich habe das bei 2 Popups gemacht. Das ist eigentlich sehr gut, weil wesentlich flexibler, aber da muss ich mich erst reinfuchsen bevor ich meine aktuellen Hauptformulare umstelle.

Zitat von: ebs17 am August 28, 2020, 17:05:36Zusätzlich: FindFirst und besonders FindLast sind nicht wirklich überzeugend:
- Prinzipiell sollte man immer einkalkulieren (prüfen), dass nichts gefunden wird. Dann ist der Datenzeiger sonstwo. => NoMatch
- Statt im Recordset zu suchen ist es effizienter, das Recordset gleich gefiltert zu öffnen.

Danke für die Tipps. Das macht Sinn. Ich hatte die RS Zeilen weitestgehend aus dem Netz via CP übernommen und fange jetzt langsam an zu verstehen, wie man mit RS arbeitet.

Zitat von: ebs17 am August 28, 2020, 17:05:36Recordsets nicht schließen ist ganz schlechter Stil und kann einem "ganz überraschend" mal gewaltig auf die Füße fallen.

War so eigentlich nicht geplant und sehe das jetzt auch. Ich dachte, ich hätte das mit dem rs.close etc am Ende abgedeckt. Macht es hier Sinn nach jedem rs.close auch ein set rs = nothing zu machen, oder set rs = nothing nur am Ende der Sub/Function/etc um den Speicher freizugeben?

Zitat von: ebs17 am August 28, 2020, 17:05:36Das andere habe ich mir nicht angesehen, bei der Sprungmarke habe ich dann rasch abgebrochen.
Es geht mir nur um die Frage, wie ich am besten via VBA das Feld mit dem Orderpfad aktualisiere. Den Rest des Codes muss sich niemand angucken, das ist nur Kontext. Im Code gibt es auch noch viel zu verbessern. Alles noch sehr stümperhaft und nach GTD-manier geschrieben :)

Wie ganz am Anfang erwähnt und im Code sichtbar: das Schreiben direkt ins Feld auf dem Formular ist wohl der richtige Weg, aber ich möchte die Funktion auch aufrufen, wenn das Formular nicht geöffnet ist (Massen-Update der Ordner). Daher die Notwendigkeit für RS oder SQL UPDATE (oder was anderes).

Alternative könnte man das mit IF machen a la "wenn das Formular geöffnet ist, dann schreib in das Feld, ansonsten RS...". Ich weiß nicht, ob das guter Stil oder sinnvoll ist.

maddhin

#4
Zitat von: Beaker s.a. am August 28, 2020, 13:28:37If Me.Dirty Then Me.Dirty = False

Wird getestet!

Danke!


EDIT:

...und ist erfolgreich getestet! Soweit ich das beurteilen kann, löst das das Problem! DANKE!

Bleibt die Frage, ob das Recordset hier das generell richtige Instrument ist.

ebs17

#5
Gebundenes Formular - Recordset - Aktionsabfrage ... alles sind RICHTIGE Instrumente.

Allerdings: Mit allen kann man Datensätze editieren, und wenn man den gleichen Datensatz gleichzeitig über mehrere Wege editiert, bekommt man einen Konflikt. Schon ganz allein für sich, da braucht man nicht mal Kollegen in einer Mehrnutzerumgebung.

In einem gebundenen Formular wird der aktuell editierte Datensatz automatisch gespeichert, wenn der Datensatz "verlassen" wird (Datensatzwechsel, Formularwechsel oder -schließen). Wenn man nicht so lange warten kann oder will, muss man einen expliziten Befehl verwenden wie z.B. die von ekkehard genannte Variante.

ZitatGenerell höre ich aus der Antwort heraus, mit ungebundenen (Feldern auf) Formularen zu arbeiten und die Daten via Recordset zu laden.
So sollte das nicht verstanden werden. Das Arbeiten mit einem gebundenen Formular ist sehr komfortabel und vielen Fällen die bessere Lösung. Man braucht da z.B. keine Codes für grundlegende Dinge, weil da einiges im Hintergrund automatisch abläuft.

Richtig ist aber auch, dass alle drei Wege zum grundlegenden Handwerkszeug (Daten aus Tabellen lesen und Daten in Tabellen schreiben) gehören und man es beherrschen sollte. Bei der konkreten Verwendung kommt es eben darauf an.

ZitatMacht es hier Sinn nach jedem rs.close auch ein set rs = nothing zu machen
Weniger.

Das Close ist sehr wichtig und sollte immer so schnell erfolgen wie möglich: Beenden des Tabellenzugriffs, Freigabe des belegten Arbeitsspeichers. Das automatische Schließen eines privat deklarierten Recordsets mit Ende der Prozedur funktioniert nicht immer und kann nachfolgend Probleme bereiten.
Objekte, die man öffnet, schließt man auch.

Das Set auf Nothing betrifft nur die Objektvariable an sich (Zeiger auf den Speicher). Das ist weniger kritisch und bei einer unmittelbaren Wiederverwendung unnötig.

Set db = CurrentDb
...
db.Close
Dieses Close ist überflüssig. Da passiert nichts, und stelle Dir vor, es würde tatsächlich passieren, was da geschrieben steht ...
Für ein Abschießen der eigenen Anwendung genügt ein ...
Application. Quit
Mit freundlichem Glück Auf!

Eberhard

maddhin

Lieber Eberhard - und natürlich auch Ekkehard,

herzlichen Dank für die ausführliche Hilfe!!

Sehr gute Info und ich verstehe jetzt deutlich mehr worauf es ankommt. Insb. die Sache mit dem Speichern und Tabellenzugriff waren sehr nützliche Erklärungen. Macht viel Sinn!

Ich werde jetzt auch fleißig RS immer schießen, denn Dein "ist ein ganz schlechter Stil" wird noch eine Weile nachklingen haha