Fragen zu Dataset->Db

  • VB.NET

Es gibt 51 Antworten in diesem Thema. Der letzte Beitrag () ist von OliverSte.

    Fragen zu Dataset->Db

    ausgelagert aus Dataset->Db ~VaporiZed

    Moin @ErfinderDesRades,
    wunderschöner Post, Danke sehr dafür.
    Ich habe mir die MySQL Lösung vorgeknöpft. In Verbindung mit einem MariaDB Server und dem mysql-connector-net-8.0.28 funktioniert das auch, beinahe so, wie du es vorführst. In deinem SqlCe Beispiel instanziierst du die Variable _Persistance in der Sub New() nach InitializeComponent(). Wann wird das aufgerufen?
    In einem anderen Thread aus 2013 habe ich von Problemen beim Initialisieren auf x64 Platform gelesen, weshalb man das in Form_Shown() machen sollte. Also tue ich so.
    ABER. Mit InitializeComponent() funktioniert gar nichts. Also die Button feuern keine Events. Ohne klappt alles ganz wunderbar.
    In allen deinen Beispielen ist das aber mit drin ???

    Ich habe die MySqlHelpers und MiniHelpers inkludiert, sonst nichts. Der Code ist erschreckend knapp, nur 4 Zeilen erledigen das mit der Database Persistierung !?!? Das kann doch nur ein Traum sein oder? Ist das wirklich schon alles?

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports MySqlHelpers
    3. Public Class Telefonbuch
    4. Private _Persistance As MySqlServerPersistance 'Class MySqlServerPersistance in MySqlHelpers.MysqlPersistance.vb
    5. Private Sub Telefonbuch_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
    6. Debug.Print(Me.Name + "_Shown")
    7. Debug.Print("Persisitiere mit ""server=localhost;uid=root;pwd=***;persistsecurityinfo=True;database=testdb""")
    8. _Persistance = New MySqlServerPersistance("server=localhost;uid=root;pwd=***;persistsecurityinfo=True;database=testdb", PhonebookDataset)
    9. End Sub
    10. Private Sub Button_Click(sender As Object, e As EventArgs) Handles bLaden.Click, bSpeichern.Click
    11. Debug.Print(DirectCast(sender, Button).Name + "_Click")
    12. Select Case True
    13. Case sender Is bLaden : _Persistance.FillAll()
    14. Case sender Is bSpeichern : _Persistance.Save()
    15. End Select
    16. System.Media.SystemSounds.Asterisk.Play()
    17. End Sub
    18. End Class


    Viele Grüße Oliver

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Marcus Gräfe“ ()

    OliverSte schrieb:

    Das kann doch nur ein Traum sein oder? Ist das wirklich schon alles?
    Tja, ich denke schon.

    OliverSte schrieb:

    Also die Button feuern keine Events.
    Woran das liegt habich keine Ahnung.
    Der Code sieht korrekt aus, vorrausgesetzt Class Telefonbuch ist ein Form, und bLaden, bSpeichern sind Buttons darauf, die man zB auch im Form-Designer dort sehen kann.

    OliverSte schrieb:

    In deinem SqlCe Beispiel instanziierst du die Variable _Persistance in der Sub New() nach InitializeComponent(). Wann wird das aufgerufen?
    Na, wenn ein Konstruktor eben aufgerufen wird. Also wenn das Form erstellt wird.
    Aber setz doch einen Haltepunkt rein, und sie selbst nach, wann das aufgerufen wird.

    Gut wäre auch, den Code zu sehen, der nicht funktioniert. So ist das alles nur Schwafel ins Blaue.

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

    Lieber @ErfinderDesRades,

    danke für die Bestätigung, dass das schon alles ist. Ich habe deine VB Helpers auch problemlos in eine C# Solution einbauen können, funktioniert wunderbar.

    Class Telefonbuch ist ein Form und alle Komponenten sind im Designer auch sichtbar (siehe Bild).
    Ich habe das Formular mit dem Designer erstellt und da wurde keine Sub New() generiert. Das wundert mich, warum hast du eine?

    Nun habe ich erst mal aus NuGet die SqlCe DLL installiert, um deine Demo-Solution überhaupt starten zu können. Ein Haltepunkt auf InitializeComponent() zeigt mir nicht, von wo das aufgerufen wird. Ist mir jetzt aber auch egal, ich habe ja nicht mal eine New() in meinem Projekt ;)

    Wenn ich nun das Ereignis Load folgendermaßen fülle, feuern die Buttons nicht mehr. Load wird vor Shown ausgeführt.

    VB.NET-Quellcode

    1. Private Sub Telefonbuch_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. InitializeComponent()
    3. End Sub


    Da aber die Komponenten zur Laufzeit gezeichnet werden, wird InitializeComponent() wohl irgendwo implizit aufgerufen und wenn ich es erneut aufrufe, klappt es nicht mehr.
    Bilder
    • Form_Telefonbuch_Designer.jpg

      24,67 kB, 405×404, 74 mal angesehen
    InitializeComponent() wird in VB irgendwie versteckt ausgeführt.
    Stell oben im Projekt-Explorer ein, dass dir alle Dateien angezeigt werden, da gibt es ein Symbol auf das du dazu klicken musst.
    Wenn du dann im Telefonbuch.Designer.vb die Zeile
    <System.Diagnostics.DebuggerStepThrough()>
    vor der Zeile Private Sub InitializeComponent()
    auskommentierst und in der Sub einen Haltepunkt setzt, siehst du, dass Telefonbuch_Load direkt danach ausgeführt wird.

    Wenn du InitializeComponent() selbst kontrolliert ausführen willst, dann kopiere den folgenden Code und füge ihn in Telefonbuch.vb über Telefonbuch_Load ein:

    VB.NET-Quellcode

    1. Public Sub New()
    2. ' This call is required by the designer.
    3. InitializeComponent()
    4. ' Add any initialization after the InitializeComponent() call.
    5. 'Type all your code here! This code will be executed before the "FormLoad" if you call your new form
    6. End Sub

    (Quelle: stackoverflow.com/questions/18…component-is-not-declared)

    Unmittelbar unter InitializeComponent() kannst du jetzt deine eigenen Inititalisierungen durchführen.

    OliverSte schrieb:

    Wenn ich nun das Ereignis Load folgendermaßen...
    Nee - so nicht.
    InitializeComponents() muss im Konstruktor aufgerufen werden - nicht im Form_Load.
    Oder sonst irgendwo anders.
    Oder - wenn du keinen Konstruktor brauchst, lass ihn weg, dann macht vb.net das unsichtbar im Hintergrund.

    Aber eben auch @Dksksms Empfehlung: Entferne das depperte DebuggerStepThrough-Attribut von InitializeComponents() - dann kannst du besser debuggen.

    Ums verständlich zu machen:
    InitializeComponents() initialisiert alle Controls, die auf dem Form zu sehen sind. Das muss so früh als irgend möglich passieren.
    InitializeComponents() ist auch besonders interessant: Wenn du im Form-Designer Änderungen an deinen Controls vornimmst, werden diese Änderungen vom FormDesigner in ganz normalen vb.net-Code übersetzt - welcher nämlich in InitializeComponents() steht.
    Spiel da mal ein bischen mit rum.

    Ansonsten muss man mit Designer-Code höchst vorsichtig umgehen - am besten gar nicht.
    DEr Code ist vom Gesichtspunkt der Wartbarkeit ziemlich miserabel, aber man kann ihn nicht verbessern, weil wenn du im Designer Wysiwyg irgendeine Kleinigkeit veränderst, generiert er die Datei neu, und deine Änderungen sind weg.
    Eigentlich ein Zufall, dass der Designer es nicht überschreibt, wenn man das depperte DebuggerStepThrough-Attribut wegnimmt - da sind wir einfach froh und dankbar für. :saint:

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

    Danke an @Dksksm und @ErfinderDesRades.

    ​wenn du keinen Konstruktor brauchst, lass ihn weg, dann macht vb.net das unsichtbar im Hintergrund

    Das mutmaßte ich bereits. Aufgrund der Probleme mit 64-Bit sollen Initialisierungen doch eh besser im Shown gemacht werden.

    Ich sag's noch mal, weil man es nicht oft genug sagen kann. Danke für deine Helper-Bibliotheken!
    Ach, da hab ich noch eine Frage.
    Du @ErfinderDesRades hast in den letzten Jahren diverse Helpers veröffentlicht.
    Ich habe da den Durchblick verloren, was nun essentiell und was wohin übergegangen ist.

    Für dieses einfache "MySQL Persistieren"-Projekt genügen MiniHelpers und MysqlHelpers aus der Solution AllTogether2021.
    Was ist mit DBExtensions, GeneralHelpers, WinFormHelpers aus AllTogether2010?
    Danke für einen kurzen Hinweis :D
    naja, das neueste ist immer das beste.
    Meine Helpers sind manchmal ungekürzt an iwelche Tuts drangehängt, die eigentlich was ganz anderes zum Thema haben.
    Mit dene kann ich halt sehr schnell und sauber entwickeln.
    Aber die ungekürzten Helpers können nie selbst Gegenstand eines Tuts sein, weil sie halt alles mögliche abdecken.
    Ich bastel da auch immer wieder dran rum - die veralten also auch.

    Hingegen so MiniHelpers konzentrieren sich auf das, was für das Tut nötig ist. Dann ist der Code nicht so viel und kann man auch verstehen, weil man ja auch sieht, wie er verwendet wird - das ist der Vorteil davon.
    Das gehört dann schon eher zum Tut, und zeigt ja auch auf, wie man eine Solution aus mehreren Projekten zusammensetzen kann, und wie Helpers-Projekte besonders geeigneten Code dann auch in anderen Solutions bereitstellen können, ohne dass man mit Copy/Paste ein Wirrsal anrichtet oder gar dasselbe mehrfach entwickelt.
    Also brauch ich i.d.R. die DBExtensions für DB-Projekte, weil noch mehr Funktionalität drin steckt?
    Und die WinFormHelpers, wenn ich mit mehreren Forms auf einem Dataset arbeiten möchte?
    Weil die MiniHelpers sind ausschließlich zum Persisitieren?
    Ja prima, Die MiniHelpers aus DbPersistance2020 sind in AllTogether2021 enthalten und werden dort sinnvoll durch MySqlHelpers ergänzt. Damit habe ich den Persistieren Teil abgedeckt.
    Die WinFormHelpers stecken im AllTogether2020 Paket. Da ist dann auch GeneralHelpers und der DbGenerator zu finden.
    Nun müsste man mal sehen, wo welche Funktion beschrieben steht, dafür gibt es ja diverse Tutorials. Tja, die Nadel im Heuhaufen und ich weiß nicht mal, wie die aussieht.

    OliverSte schrieb:

    Nun müsste man mal sehen, wo welche Funktion beschrieben steht, dafür gibt es ja diverse Tutorials.
    Ja, sorry, das ist ein Problem.
    GeneralHelpers und WinformHelpers decken immens viel ab, aber eine systematische Dokumentation dazu gibt es nicht.
    Wüsste ich nichtmal, wie eine solche aufbauen, dass man alles darin findet, und es gleichzeitig aber auch versteht.

    Was man aber verstehen kann ist, dass Helpers-Bibliotheken teilweise aufeinander aufbauen:
    • GeneralHelpers - deckt allgemeine Algorithmen und Klassen ab - benötigt keine Winforms.
    • WinformHelpers - unterstützt die Arbeit mit Winforms-Steuerelementen - insbesondere Databinding rauf und runter.
      WinformHelpers benötigt einige Algorithmen und Klassen aus GeneralHelpers
    • MiniHelpers und MySqlHelpers unterstützen das Ansprechen von MySql
      brauchen WinformHelpers und GeneralHelpers nicht.
    Wenn man alle vier verwendet, so hat man ein paar Sachen sowohl in MiniHelpers als auch in GeneralHelpers - das wäre architektonisch nicht ganz sauber, und man sollte Dinge aus MiniHelpers entfernen, die es ebenso auch in GeneralHelpers schon gibt.
    Oder halt damit leben - solange man sich nicht gut genug damit auskennt.
    Nun habe ich das einfach Testprojekt mit nur einer einzigen Tabelle fertig. Jetzt geht's an Relationen.
    Ziel ist eine Terminverwaltung. Ein Termin besteht aus einem Kunden, einem Mitarbeiter, sowie einem Datum und einer Zeit.
    Auf dem MySQL Server sind die Datentypen für Datum DATE und Zeit TIME. Im Dataset habe ich System.DateTime eingestellt. Ja, da geht es schon los, denn ein DateTime Objekt enthält Datum UND Zeit. Beim Laden mit _Persistance.FillAll() rummst es dann auch gleich in der Zeile:

    _RankedTables.ForEach(Sub(tb) GetAdapter(tb).Fill(tb)) von @ErfinderDesRades seinem DbPersistanceBase

    System.ArgumentException: "Das Objekt des Typs "System.TimeSpan" kann nicht in Typ "System.IConvertible" umgewandelt werden.<16:00:00> konnte nicht in der uhrzeit-Spalte gespeichert werden. Erwarteter Typ: DateTime."

    InvalidCastException: Das Objekt des Typs "System.TimeSpan" kann nicht in Typ "System.IConvertible" umgewandelt werden.

    Nehm ich da besser String?
    tja, weiss ich auch nicht.
    Ich kenn mich nur bischen mit Access und MS-SqlServer aus. Da gibt es date-Datentypen, die mit .Net-DateTime kompatibel sind.
    Ich kann mir eigentlich nicht vorstellen, dass MySql keinen solchen Datentyp anbietet.
    string ist jdfs. denkbar ungeeignet - man will Datumse ja sortieren, und will mit ihnen rechnen.

    MrTrebron schrieb:

    Sind die Datentypen in der MySQL vorgegebenen?
    na, wohl nicht - es ist ja ein Testprojekt

    MrTrebron schrieb:

    Warum nicht im Dataset die gleichen Datentypen?
    "Gleichen Datentyp" kann es ja nicht geben - Sql und .Net-Framework sind ja verschiedene Sprachen.
    Es kann allenfalls kompatible Datentypen geben, und wie gesagt ich bin sicher, dass MySql auch (mindestens) einen Datentyp bereitstellt, der zu .Net-DateTime kompatibel ist.
    Ob es ausserdem einen TimeSpan-kompatiblen MySql-Datentyp gibt weiss ich nicht - aber vlt gibts da ja Doku zu.

    @OliverSte:
    Im Dataset habe ich System.DateTime eingestellt.
    Hmm - wenn man die Fehlermeldung ganz naiv liest, steht da aber was anderes

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

    Ich hab da mal Screenshots vom MySql und Dataset angehängt.
    Der Datentyp im Dataset ist DateTime, in MySql TIME. Damit produziere ich die o.g. Fehlermeldung. Frag mich nicht, warum da was von TimeSpan steht, bzw. woher das kommt.
    Nun habe ich im Dataset der Uhrzeit einfach mal den Datentyp TimeSpan gegeben. Und schwupps, läuft :D

    DatasetMySql
    DateTimeDATE
    TimeSpanTIME
    Bilder
    • MySQL Time.jpg

      33,17 kB, 589×289, 61 mal angesehen
    • Dataset DateTime.jpg

      135,58 kB, 943×662, 65 mal angesehen

    OliverSte schrieb:

    Im Dataset habe ich System.DateTime eingestellt.
    Das ist wohl das eigentliche Problem.
    Weil ein Dataset sollte man besser nicht händisch so hinbosseln müssen, dasses zur DB passt.
    Also entweder das Dataset aus der DB generieren, oder andersrum aus dem Dataset eine DB generieren.

    Händisch es hinfummeln erfordert ziemlich genaue Kenntnisse, die kaum irgendwo dokumentiert sind, und es sind hunderte von Einstellungen zu tätigen, und jede mit eigener Fehler-Gefahr.

    Aber gut - die Selbst-Bastelei ist nicht immer zu vermeiden - zB die zur Dataset-Generierung nötigen Datenbank-Connectoren aller Nicht-MS-Datenbank-Systeme sind zickig zu installieren.

    OliverSte schrieb:

    Der Datentyp im Dataset ist DateTime, in MySql TIME. Damit produziere ich die o.g. Fehlermeldung. Frag mich nicht, warum da was von TimeSpan steht, bzw. woher das kommt.
    Brauch ich nicht - ich kanns mir denken:
    Du hast nu selbst gelernt, dass MySql-TIME zu .Net-DateTime nicht kompatibel ist, sondern zu .Net-TimeSpan.
    Der DataAdapter scheint dieser Kompatiblität entsprechend den TIME-Wert intern in einen Timespan zu lesen. In einem zweiten Schritt versucht er, den Timespan auf den im Dataset vorgegebenen Typ zu konvertieren: auf DateTime.
    Dazu würde er - wenn es ginge - die IConvertible-Schnittstelle verwenden wollen.
    Tja - dummerweise implementiert TimeSpan nicht IConvertible - ja, und genau das sagt die Fehlermeldung.
    (Zur Info: Andere Dataset-Datentypen, wie String, DateTime, Double,... - die implementieren alle IConvertible - gucks dir im ObjectBrowser an.
    Aber Timespan numa nicht.)



    Ansonsten finde ich dein Datenmodell zwar funktional plausibel, aber vonne Benamung her doch gegen so einiges, was ich unbedingt als BestPractice empfehlen würde.
    Nämlich Public Member werden in .Net gross geschrieben (Pascal-case, um genau zu sein)
    Und Db-Tabellen sollte man singular benamen
    Andernfalls erscheinen die einzelnen Datensätze darin plural benamt, und ein einzelner Datensatz ist numa nicht Plural.
    gugge codeproject.com/Articles/10331…eginners#Model-Guidelines

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

    Danke @ErfinderDesRades für den Schlag auf den Hinterkopf.
    Ich hatte die Benamung "alles klein", weil es da ja unterschiedliches Handling auf Windows und Linux gibt. Dachte, mit alles klein sei ich auf der sicheren Seite. Bin ich aber wahrscheinlich auch, wenn ich die Tables korrekt im Code anspreche.

    Nebenbei noch ein kleines Problemchen mit DefaultValues bei TimeSpan und DateTime gelöst ;) Kannste nämlich nicht in den Dataset Designer eintragen.
    Stattdessen lässt man da <DBNull> stehen und schreibt in Form_Load:

    VB.NET-Quellcode

    1. TermineDts.Termin.uhrzeitColumn.DefaultValue = TimeOfDay.TimeOfDay
    2. TermineDts.Termin.terminColumn.DefaultValue = Date.Today

    damit das in Verbindung mit AllowDBNull=False richtig gut funktioniert.
    Bilder
    • Dataset TermineDts.jpg

      159,6 kB, 943×736, 64 mal angesehen
    • Termine_Form.jpg

      58,27 kB, 575×653, 53 mal angesehen

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

    Jo, dann nur noch die Spalte Termin.Termin. Das ist doch kein Termin, sondern da steht doch sicherlich nur das Datum des Termins drinne.
    Dann sollte sie doch besser Datum heissen, odr?

    Jo, prinzipiell kann man schon auch DefaultValues für DateTime und Timespan im Dataset eintragen.
    Aber Date.ToDay oder Date.Now natürlich nicht, weil das sind ja keine Konstanten.