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.
schreibkonflikt.JPG
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
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
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.
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.
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.
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.CloseDieses 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
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