Typisierte relationale Datenbank füllen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Typisierte relationale Datenbank füllen

    Hallo in die Runde,

    ich habe das Dataset dts_Medienkopieren mit folgendem Aufbau:



    Bei der Erstellung des Dataset habe ich mich an das Tutorial zu den Vier Views von @ErfinderDesRades gehalten.
    die vier Views auf Video

    Mein Problem ist nun weniger das Erstellen der Views vielmehr das Füllen der untergeordneten Datatable "Medien".
    Die übergeordneten fülle ich wie folgt:

    VB.NET-Quellcode

    1. With Dts_MedienKopieren
    2. .Clear()
    3. For Each s In liDateiartKopieren
    4. .Dateiart.AddDateiartRow(s)
    5. Next
    6. For Each s In liDateiendungKopieren
    7. .Dateiendung.AddDateiendungRow(s)
    8. Next
    9. For Each s In liDatumKopieren
    10. .Datum.AddDatumRow(s)
    11. Next
    12. End With


    Gesamter Code (den Umweg über List werde ich noch entfernen):
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Sub MedienLadenDatenbank()
    2. Dim liMedienKopieren As New List(Of Mediendatei)
    3. Dim liDateiendungKopieren As New List(Of String)
    4. Dim liDateiartKopieren As New List(Of String)
    5. Dim liDatumKopieren As New List(Of Date)
    6. Dim MedienDateienKopieren() As String
    7. liMedienKopieren.Clear()
    8. liDateiendungKopieren.Clear()
    9. liDateiartKopieren.Clear()
    10. liDatumKopieren.Clear()
    11. QuellverzeichnisKopieren = Lbl_QuellverzeichnisKopieren.Text 'Übergibt das Quellverzeichnis vom UI an die Variable
    12. MedienDateienKopieren = Directory.GetFiles(QuellverzeichnisKopieren) 'Lädt die Dateien aus dem Verzeichnis in die Dateiliste
    13. For Each s As String In MedienDateienKopieren 'Durchläuft jede Datei in der Dateiliste
    14. liMedienKopieren.Add(New Mediendatei(s))
    15. Next
    16. For Each s As Mediendatei In liMedienKopieren
    17. If Not liDateiartKopieren.Contains(s.Dateityp) Then
    18. liDateiartKopieren.Add(s.Dateityp)
    19. End If
    20. If Not liDateiendungKopieren.Contains(s.Dateiendung) Then
    21. liDateiendungKopieren.Add(s.Dateiendung)
    22. End If
    23. If Not liDatumKopieren.Contains(s.Aufnahmedatum) Then
    24. liDatumKopieren.Add(s.Aufnahmedatum)
    25. End If
    26. Next
    27. liDateiartKopieren.Sort()
    28. liDateiendungKopieren.Sort()
    29. liDatumKopieren.Sort()
    30. With Dts_MedienKopieren
    31. .Clear()
    32. For Each s In liDateiartKopieren
    33. .Dateiart.AddDateiartRow(s)
    34. Next
    35. For Each s In liDateiendungKopieren
    36. .Dateiendung.AddDateiendungRow(s)
    37. Next
    38. For Each s In liDatumKopieren
    39. .Datum.AddDatumRow(s)
    40. Next
    41. End With
    42. End Sub





    So weit ich es verstehe müsste ich für die untergeordnete Datatable ja wie folgt vorgehen:

    VB.NET-Quellcode

    1. .Medien.AddMedienRow(Name, Pfad, Groesse, DateiendungID, DateiartID, DatumID)


    Die übergebenen Variablen sind hier nur als Platzhalter zu sehen.
    Die ersten drei Parameter sind kein Problem, da hier keine Beziehung zu einer anderen Datatable besteht. Bei den letzten drei jedoch schon. Hier muss ja die passende ID (Primärschlüssel) der übergeordneten Tabelle übergeben werden.
    Dieser ist mir allerdings unbekannt. Ich kenne nur den Inhalt der zweiten Spalten der übergeordneten Tabellen.

    Den einzigen Ansatz den ich nun hätte wäre über Linq (müsste ich mir noch aneignen) mit der bekannten Endung in der Datatable "Dateiendung" zu suchen um den richtigen Primärschlüssel dieser Tabelle in "Medien" eintragen zu können. Für die zwei weiteren Tabellen analog.

    Klingt für mich nicht gerade elegant.


    Ich hoffe meine Beschreibung ist halbwegs verständlich.

    Vielen Dank für eure Unterstützung.

    D@niel
    Zunächstmal:

    VB.NET-Quellcode

    1. .Medien.AddMedienRow(Name, Pfad, Groesse, DateiendungID, DateiartID, DatumID)
    sollte nicht gehen.
    Statt der 3 Fremdschlüssel verlangt .AddMedienRow die Angabe der übergeordneten typisierten DataRows - richtig?

    Jo, und der Algo ist nicht ganz trivial, weil du hast eine flache Liste, und willst daraus eine hierarchische Struktur bilden, und zwar gleich bestehend aus 3 Hierarchien.
    Kannst du vlt. ein Sample-Projekt anhängen, inklusive mw. 50 beispielDateien?
    Dann könnte ich mich übers WE dran versuchen
    Danke für deine Antwort und dein Angebot.

    ErfinderDesRades schrieb:

    Statt der 3 Fremdschlüssel verlangt .AddMedienRow die Angabe der übergeordneten typisierten DataRows - richtig?


    Ja - ich hätte es schon gerne so gemacht wie in deinem Tut bei AddArticleRow... bzw. Dim rwCat...
    Im Gegensatz zu dir weiß ich aber nicht z.B. iCat.

    Auszug aus dem Code deines Tutorials.

    VB.NET-Quellcode

    1. For iCat = 0 To 3
    2. For iDeli = 2 To 6
    3. Dim rwCat = .Category(iCat)
    4. Dim rwDeli = .Deliverer(iDeli)
    5. Dim name = String.Join("_", "Article" & iArticle, iCat, iDeli)
    6. Dim price = 3D + CDec(iCat * iDeli) / 100
    7. .Article.AddArticleRow(name, price, rwCat, rwDeli)
    8. iArticle += 1
    9. Next
    10. Next


    Wenn man jetzt mal z.B. meine Tabelle Dateiendung nimmt und die sähe wie folgt aus:

    ID
    Endung
    -1
    mp4
    -2
    jpg
    -3
    png

    Und ich hätte eine Bilddatei die ich in die untergeordnete Datatable schreiben möchte mit der Dateiendung jpg dann weiß ich ja zunächst nicht an welcher Positon die entsprechende Endung in der übergeordneten Datatable steht. Wenn ich das wüsste würde ich es so wie du in deinem Tut machen.

    ErfinderDesRades schrieb:

    Jo, und der Algo ist nicht ganz trivial, weil du hast eine flache Liste, und willst daraus eine hierarchische Struktur bilden, und zwar gleich bestehend aus 3 Hierarchien.


    An der Stelle kann ich dir nicht ganz folgen. Mein Gedanke war bei der gewählten Struktur war, dass allle Informationen die für mehrere Dateien zutreffen in eine separate Datatable ausgelagert werden.

    Aufgrund der Dateigröße habe ich die von mir verwendeten Beispieldateien extern hochgeladen (Ich weiß wird nicht gern gesehen. Grundsätzlich funktionieren sämtliche Bilddateien)

    Onedrive:
    1drv.ms/u/s!AjWrZiFYdnN7gYde0tyw9wPTpaTZDw?e=00Z9fE

    Das Verzeichnis in dem die Bilder liegen ist nicht relevant, da es zur Laufzeit vorgegeben wird.

    Das Projekt habe ich hier als Anhang hinzugefügt.
    Bilder Manager.zip

    Vielen Dank

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „D@niel“ () aus folgendem Grund: Rechtschreibung

    Dringende Empfehlung: Visual Studio - Empfohlene Einstellungen
    Ich hab schoma das Projekt auf Strict On gestellt, und den vb6-Namespace-GeneralImport entfernt.
    Die vb6-Namespace-Verwendungen habe ich kenntlich gemacht - sollteste beizeiten mal durch vb.net-Pendante ersetzen - Hinweis dazu gibts auch im angegebenen Tut.



    Dann habich ein flexibles Layout drangemacht, nach diesem Kochrezept: Layout in Windows.Forms



    Dann habich eine partiale Dataset-Klasse angelegt, wo ich empfehlen würde, Businesslogik drin anzulegen.
    Das Einlesen aus dem Dateisystem habich da schoma reingemacht:

    VB.NET-Quellcode

    1. Imports System.IO
    2. Partial Public Class dts_MedienKopieren
    3. Public Sub FillFromFilesystem(di As DirectoryInfo)
    4. Me.Clear()
    5. Dim medien = di.GetFiles.Select(Function(fi) New Mediendatei(fi.FullName)).ToList
    6. Dim dateiendungs = medien.Select(Function(md) md.Dateiendung).Distinct.ToList
    7. Dim dateityps = medien.Select(Function(md) md.Dateityp).Distinct.ToList
    8. Dim aufnahmeDatums = medien.Select(Function(md) md.Aufnahmedatum).Distinct.ToList
    9. Dim dicDateiendung = dateiendungs.ToDictionary(Function(x) x, Function(x) Me.Dateiendung.AddDateiendungRow(x))
    10. Dim dicDateityp = dateityps.ToDictionary(Function(x) x, Function(x) Me.Dateiart.AddDateiartRow(x))
    11. Dim dicAufnahmedatum = aufnahmeDatums.ToDictionary(Function(x) x, Function(x) Me.Datum.AddDatumRow(x))
    12. For Each md In medien
    13. Me.Medien.AddMedienRow(md.Name, md.Pfad, md.Groesse, dicDateiendung(md.Dateiendung), dicDateityp(md.Dateityp), dicAufnahmedatum(md.Aufnahmedatum))
    14. Next
    15. End Sub
    16. End Class
    also zunächstmal erzeuge ich alle medien.
    Aus den Medien extrahiere ich dann mit Distinct die Werte der überzuordnenden Entitäten: DateiEndungen, DateiArt, Datum (zeilen #8-10).
    Aus den Entitäten-Werten erzeuge ich Dictionaries, wo zum jeweiligen Wert auch gleich die richtige typisierte DataRow zugeordnet ist (zeilen #11-13).
    Jo, und dann habich alle übergeordneten DataRows, und kann sie auch bequem über die Dictionaries zugreifen - also da kann ich nett die MedienRows auch anlegen.



    Dadurch verdampft der FormCode bis auf einen ziemlichkleinen Rest:

    VB.NET-Quellcode

    1. Imports System.IO
    2. Public Class frmMain
    3. Private Sub Btn_QuellverzeichnisKopieren_Click(sender As Object, e As EventArgs) Handles Btn_QuellverzeichnisKopieren.Click
    4. If FolderBrowser.ShowDialog() = Windows.Forms.DialogResult.OK Then
    5. Lbl_QuellverzeichnisKopieren.Text = FolderBrowser.SelectedPath
    6. End If
    7. End Sub
    8. Private Sub Btn_ZielverzeichnisKopieren_Click(sender As Object, e As EventArgs) Handles Btn_ZielverzeichnisKopieren.Click
    9. If FolderBrowser.ShowDialog() = Windows.Forms.DialogResult.OK Then
    10. Lbl_ZielverzeichnisKopieren.Text = FolderBrowser.SelectedPath
    11. End If
    12. End Sub
    13. Private Sub Btn_EinlesenKopieren_Click(sender As Object, e As EventArgs) Handles Btn_EinlesenKopieren.Click
    14. Dts_MedienKopieren.FillFromFilesystem(New DirectoryInfo(Lbl_QuellverzeichnisKopieren.Text))
    15. End Sub
    16. Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles Me.Load
    17. FolderBrowser.SelectedPath = Path.GetFullPath("..\..\..")
    18. End Sub
    19. End Class
    Der Form habich übrigens einen neuen Namen gegeben, der nicht im NamensKonflikt mit dem RootNamespace steht.
    Ja, und natürlich alle unnötigen Leerzeilen entfernt. Und auch die Regions. Weil ich will den Code immer sehen, möglichst auf einem Bildschirm. Unnötige Leerzeilen machen die Dateien zu lang dafür, und Regions beabsichtigen es ja gradezu, den Code vor mir zu verbergen.
    Jo - ich denke, dassis schoma einiges zum drüber nachdenken...
    Bei Fragen immer fragen.
    Dateien

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()

    @ErfinderDesRades vielen Dank für deine Mühen und die Zeit die du in die Korrektur meines Codes investiert hast.
    Ich bin leider erst jetzt dazu gekommen allen Stichpunkten die du aufgeworfen hast nach zu gehen.

    Nachfolgend nur ein paar kleine Rückfragen um Missverständnissen meinerseits vorzubeugen.

    1. Partial Class

    ErfinderDesRades schrieb:

    Dann habich eine partiale Dataset-Klasse angelegt, wo ich empfehlen würde, Businesslogik drin anzulegen.


    Ich vermute du meinst damit sämtlichen Code der sich auf das Dataset bezieht in diese Klasse zu verbannen und nicht ins Userform.

    Was ich an daran nicht verstehe ist warum du eine Partial Class vorschlägst.

    Aus folgender Quelle entnehme ich, dass der Vorteil der Partial Class im wesentlichen darin besteht, dass sie nicht auf eine Datei begrenzt ist.
    docs.microsoft.com/de-de/visua…tial-classes?view=vs-2019

    Wo der Vorteil in diesem konkreten Fall liegt sehe ich nicht (nicht aus Ablehnung sondern vermutlich aus mangelndem Wissen heraus).


    2. Lambda Ausdruck
    Meine Annahme ist richtig, dass es sich in den Funktionsaufrufen in der Klasse um Lambda-Ausdrücke handelt? Ein Thema was ich bisher zumindest vernachlässigt hatte.


    Alles Weitere waren wie gesagt hilfreiche Stichpunkte für Recherchen.
    Irgendwie drängt sich mir aber der Verdacht auf, dass es nicht der letzte Beitrag zu dem Projekt war :whistling:
    Zu 1) Guck die Solution an: Es ist sonnenklar, wo BusinessLogik hingehört.
    Die Methode FillFromFilesystem() gehört definitiv nicht in den Form-Code (der übrignes auch eine Partiale Klasse ist, wenn du mal hinschaust - achmist - in vb sieht man das nicht, da sieht man das erst, wenn man in die Form.Designer.vb guckt).
    wie dem auch sei - FillFromFilesystem() hat im Form-Code nichts verloren, weil es kein Control in irgendeiner Weise anfasst.
    Aber mit vier verschiedenen typisierten DataTables wird dort hantiert (Zeilen 11,12, 13, 15).
    Also wohin damit?
    Das ist eine Architektur-Frage. Klar kann man FillFromFilesystem() auch in den Form-Code legen. Sämtliche Methoden, dies gibt und noch geben wird, kann man da reinstopfen.
    Recht bald ist dein Form-Code dann paartausend Zeilen lang, und du findest nichts mehr wieder.

    Zu 2) Ich glaub, die richtige Bezeichnung ist "anonyme Methode". Function(md) md.Aufnahmedatum ist ja eine eigene kleine Function: Man gibt ein md AS Mediendatei hinein und erhält ein Aufnahmedatum As Date zurück. Die Methode hat aber keinen Namen wie eine normale Funktion. Deswegen ists eine "anonyme Methode".
    Zu Begriffen (zb Methode, Funktion) empfehle ich: Grundlagen: Fachbegriffe - ist enorm wichtig, wirklich zu wissen, wovon der andere redet - nicht nach Gefühl sich was zusammenreimen.

    "Lambda-Ausdruck" - hat in meim Kopf nun gar keine Definition.
    Ich kenne anonyme Methoden, Linq-Expressions, Linq-Extensions.
    Lambda - das war mal son dolles Mathematisches Kalkül, das "Lambda-Kalkül". Das hat enorm dolle was gebracht zur Entwicklung höherer Programmiersprachen, aber was das genau ist, weiss ich nicht.
    Kann ich auch nicht wissen, denn zuvor müsste ich noch lernen, was ein Mathematiker überhaupt unter einem "Kalkül" versteht. Und darauf aufbauend dann erlernen, was nun das Lambda am Lambda-Kalkül ist. Da hätte ich wohl 5 Semester zu studieren, und am Ende wüsste ich wohl immer noch nicht, wasn nun ein "Lambda-Ausdruck" sein soll.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()