MySQL Verbindung Fehler

  • VB.NET

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von Patrick1993.

    MySQL Verbindung Fehler

    Ich bin gerade ein wenig mit MySQL am spielen da ich gerade Programmieren lerne und habe mir folgenden Code geschrieben:

    VB.NET-Quellcode

    1. Imports MySql.Data.MySqlClient
    2. Public Class Form1
    3. Private Sub Label1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Label1.Click
    4. Dim con As New MySqlConnection
    5. Dim cmd As New MySqlCommand
    6. con.ConnectionString = ("server=81.20.134.98; uid=text; password=++++; database=test;")
    7. cmd.Connection = con
    8. con.Open()
    9. cmd.CommandText = "INSERT INTO test"("test")
    10. cmd.CommandText = "SELECT test FROM test"
    11. con.Close()
    12. End Sub
    13. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    14. Dim con As New MySqlConnection
    15. Dim cmd As New MySqlCommand
    16. con.ConnectionString = ("server=81.20.134.98; uid=text; password=++++; database=test;")
    17. cmd.Connection = con
    18. con.Open()
    19. cmd.CommandText = "INSERT INTO test"("test")
    20. cmd.CommandText = "SELECT test FROM test"
    21. End Sub
    22. End Class


    Nun bekomme ich eine Meldung wenn ich die 2 Exe Datein starte. im Grunde genommen kommt die Fehlermeldung sobald ich auf den Button klicke der den Inhalt der Tabelle ausgeben soll.

    Das ist die Meldung:
    Unbehandelte Ausname in der Anwendung. Klicken Sie auf "Weiter" um den Fehler zu Ignirieren und die Anwendung fortzusetzen. Wenn Sie aus "Beenden" klicken wir die Anwendung sofort beendet.
    Access denied for user 'text@95.214.44.119'(using password: YES).

    Kann es vielleicht daran liegen das mein V-Server keinen Externen Datenbankzugriff zulässt ?? Denn ich weiss nicht 100% ob ich Externen Zugriff auf die Datenbank meines Servers habe da ich mich noch nie Extern Einloggen bzw Verbinden musste

    Falls an dem Code was falsch sein sollte würde ich mich freuen wenn ich einen Hinweis bekäme um zu schauen ob ich es berichtigen kann.

    Zum Verbindungsfehler kann ich Dir wenig sagen, dafür aber zum code. ^^

    VB.NET-Quellcode

    1. con.Open()
    2. cmd.CommandText = "INSERT INTO test"("test")
    3. cmd.CommandText = "SELECT test FROM test"
    4. con.Close()


    Was passiert da?

    Du öffnest eine Connection, weisst einem Command-Object einen Text zu und schliesst die Connection wieder.

    Was fehlt da?

    1. Das dem Command-Object die Connection zugewiesen wird das es benutzen soll
    2. Das das als CommandText zugewiesene SQL-Stament auch ausgeführt wird

    Und zu 2.) beachte das es Unterschiede gibt zwischen ExecuteNonQuery und Execute ... als kleiner Tipp was ich meine: Guck Dir die Rückgaben der beiden Methoden an.

    Dann noch eine Anmerkung zum grundsätzlichen Programmieren:

    Folgenden Merksatz bitte auswendig lernen und Dir solange vorsagen bis Du nichts anders mehr denken kannst:

    Man darf keinen redunanten Code schreiben ... NIE und unter gar KEINEN Umständen. Kommt ein identischer Code mehr als einmal vor oder kann ein Code möglicherweise auch an anderen Programmstellen genutzt werden, dann wird der sofort ausgelagert und gekapselt.

    Von diesem Merksatz gibt es KEINE Ausnahme. ;)

    Nächster Merksatz:

    Eine Methode (Sub oder Funktion) erledigt genau 1 Aufgabe und keine mehr. Jede weitere Aufgabe gehört in eine eigene Methode ausgelagert.

    In dem Sinne gehört die Erstellung und das Handling der Verbindung auf alle Fälle aus dem Code verbannt und ausgelagert gekapselt. Gerade für Zugriffsobjekte bieten sich immer eigene Klassen an die die komplette Connection handeln.

    Das habe ich auch nur wegen dieser Aussage mal so deutlich geschrieben:


    ... da ich gerade Programmieren lerne ...


    Wenn Du Dir das gleich von Anfang an angewöhnst (und dazu gleichmal mit OOP befassen) dann ersparst Du Dir viel Kummer, Leid, Ärger und Arbeit. ;)

    Gruß

    Rainer
    Was fehlt da?

    1. Das dem Command-Object die Connection zugewiesen wird das es benutzen soll
    2. Das das als CommandText zugewiesene SQL-Stament auch ausgeführt wird


    1. Das mache ich wie ??
    2. Das geht wie ??

    Und zu 2.) beachte das es Unterschiede gibt zwischen ExecuteNonQuery und Execute ... als kleiner Tipp was ich meine: Guck Dir die Rückgaben der beiden Methoden an.


    Welche Unterschiede ??

    Man darf keinen redunanten Code schreiben ... NIE und unter gar KEINEN Umständen. Kommt ein identischer Code mehr als einmal vor oder kann ein Code möglicherweise auch an anderen Programmstellen genutzt werden, dann wird der sofort ausgelagert und gekapselt.


    Was heisst redunanten Code schreiben ...??

    Eine Methode (Sub oder Funktion) erledigt genau 1 Aufgabe und keine mehr. Jede weitere Aufgabe gehört in eine eigene Methode ausgelagert.

    Also muss ich Pro Button,Label etc eine neue Function anlegen ??

    In dem Sinne gehört die Erstellung und das Handling der Verbindung auf alle Fälle aus dem Code verbannt und ausgelagert gekapselt. Gerade für Zugriffsobjekte bieten sich immer eigene Klassen an die die komplette Connection handeln.

    Also eine eigene Klasse für die Verbindung schreiben z.b. Consolenanwedung ??

    Wenn Du Dir das gleich von Anfang an angewöhnst (und dazu gleichmal mit OOP befassen) dann ersparst Du Dir viel Kummer, Leid, Ärger und Arbeit.

    ich will mir erstmal Visual Basic an sich aneigenen danach gehts Stück für Stück weiter

    Patrick1993 schrieb:



    1. Das mache ich wie ??
    2. Das geht wie ??

    Welche Unterschiede ??


    Guck Dir die Methoden an die das Command-Object zur Verfügung stellt und Du wirst finden was Du brauchst. ;)

    Ich kenne die Object-Modelle des MySQLClient auch nicht genau und müsste daher genau wie Du auch nachgucken. Aber das ist der Trick beim Programmieren ... man muss seltenst die Object-Modelle direkt in- und auswendig beherrschen um im groben zu wissen wie sie funktionieren sondern nur das Grundprinzip von Objecten und Eigenschaften/Methoden verstehen und dann wissen wo man nachgucken muss.

    Das Connection-Object und das Command-Object sind halt unabhängige Objekte die nichts voneinander wissen. Das Connection-Object "erstellt/hält" die Verbindung zur Datenbank und das Command-Object führt Command auf die Datenbank aus ... bloss wenn das Command-Object nix vom Connection-Object weiss woher soll es dann wissen auf welche Datenbank. Ganz Logisch. Also muss es zwangsläufig eine Methode/Eigenschaft geben die dem Command-Object die zu nutzende Verbindung zu weisst.

    Alle verfügbaren Mehtoden/Eigenschaften werden Dir ja dank Intellisense angezeigt und sogar dazu noch mit wunderbaren Tooltips die die Infos zu der Methode/Eigenschaften beinhalten. Also kurz: Guck dort einfach nach. Oder guck gleich im Objektkatalog nach was angeboten wird.

    Und genauso bekommst Du bei der Anwendung von CommandText per Intellisense mit Tooltip mitgeteilt das das eine Eigenschaft ist. Da eine Eigenschaft nur Zustände in der Klasse setzt/ausliest ist in diesem Fall (Ausführung eines SQL-Commands) von vorneherein absolut klar das es noch irgendwo eine Methode geben muss die das SQL-Command ausführt. Gut, man weiss dann irgendwann das sowas meistens Execute oder so heisst und wenn man grundsätzlich mal mit DB's gearbeitet hat weiss man auch das es meistens 2 Execute-Methoden gibt die sich in einem kleinen aber wichtigen Detail unterscheiden.

    Wenn Du die Methoden in Intellisense oder im Objektkatalog gefunden hast dann wirst Du auch sehen das und wo sie sich unterscheiden. ;)

    Und nein ... ich schreib es Dir nicht (und ich hoffe auch kein anderer) denn wenn Du einmal weisst wie man Infos wie etwas anzuwenden ist schnell und einfach in IntelliSense oder dem Objektkatalog findet ist Dir damit für die Zukunft mehr geholfen und Du ersparst Dir sehr sehr viel Such-/Fragearbeit. ;)


    Was heisst redunanten Code schreiben ...??


    Code der sich wiederholt. In Deinem Eingangspost hast Du in den beiden Sub's genau den gleichen Code stehen der dort genau das gleiche tut: Eine Verbindung zur DB aufbauen.

    Das ist Code-Redunanz die es unter allen Umständen zu vermeiden gilt. Kurzes Beispiel damit es einprägsam wird wieso: Stell Dir vor Du hast ein größeres Projekt und an 60 verschiedenen Stellen den Datenbankzugriff jedesmal genauso hergestellt wie bei den 2 Sub's aus Deinem Opener-Post. Jetzt kommt eine Änderung am Zugriff ... z.B. Name der DB und/oder Speicherort und/oder Passwort ändern sich oder gar Umstellung des DBMS von MySQL auf MSSQL .

    Dann musst Du an 60 Stellen den DB-Zugriff ändern/anpassen. Deswegen vermeidet man eben rednunanten Code, lagert es aus und kapselt das gleich separat und dann braucht man Änderungen immer nur an einer einzigen Stellen durchzuführen.


    Also muss ich Pro Button,Label etc eine neue Function anlegen ??


    Nö ... wieso? Gilt zwar bei vielen als guter Stil Code-Ausführung nicht direkt in die Event-Handler zu legen, sondern den als separate Function zu gestalten ... aber es ist nicht zwingend. Zwingend ist nur alles wo mehr als 1 Aufgabe vorkommt das soweit in separate Funktionen zu zerlegen das jede Funktion tatsächlich nur 1 Aufgabe erledigt.

    Wenn Du Dir Deinen Eingangs-Code ansiehst und überlegst Du hättest nun eine separate Funktion die die Connection aufbaut/hält und eine separate Funktion die darauf Commands ausführt und wirst Du ziemlich schnell den Vorteil erkennen ... Du müsstest in den beiden Event-Subs den Code nicht doppelt schreiben sondern nur den Aufruf der Funktionen und Änderungen müsstest Du nur an einer Stelle vornehmen. Darum geht es.


    Also eine eigene Klasse für die Verbindung schreiben z.b. Consolenanwedung ??


    Eigene Klasse ja absolut. Aber wieso Konsolenanwendung? Eine eigene Klasse kannst du doch direkt in Deiner App erstellen und verwenden.


    ich will mir erstmal Visual Basic an sich aneigenen danach gehts Stück für Stück weiter


    Glaub mir ... Syntax ist nicht das Problem. Die Syntax einer Sprache hat man sich schnell angeeignet (wenn man nicht ganz untallentiert ist). Das große Problem ist immer das Software-Design an sich. Daher lerne bei VB.NET gleich OOP mit ... es ist zwar am Anfang dann etwas komplizierter, aber glaub mir Du wirst dafür im nachhinein viiiel mehr Zeit und Ärger einsparen als was Du am Anfang mehr Aufwand hast.

    Die meisten Probleme tauchen nicht auf weil die Leute die Syntax nicht beherrschen, sondern weil das Anwendungs-Design überhaupt nicht passt oder andersrum: Die meisten Probleme löst man beim Programmieren nicht mit einer genial geilen Syntax (die ist meistens immer sehr simple) sondern mit dem richtigen Software-Design/-Architecture. Das ist das gleiche wie mit Datenbanken ... wer nur den Umgang mit der Datenbank lernt ohne sich intensiv mit ERM auseinander gesetzt zu haben der wird niemals richtig mit einer Datenbank umgehen können und sich immer wundern wieso irgendwas nicht geht und alles immer anderes funktioniert als bei anderen. ^^

    Gruß

    Rainer
    Ich werde mich dann gleich mal hinter klemmen und ein wenig "Spielen" (wenn man es so sagen darf) und mir die "Methoden etc" mal anschauen und mal "Experimentieren" wie ich den Fehler behoben bekomme.

    Wenn ich den Code also die Verbindung in eine Sub New () packe und dort die Verbindung via Dim y AS New MySQLConnection = "Hier die Verbindungsdaten" speichern tu sollte es doch im Endeffekt reichen ohne ständig den Code abzuändern wenn änderungen an der Datenbank kommen oder liege ich falsch ??

    Soviel ich bis jetzt gelesen habe were es wirklich besser in meiner Anwendung eine Klasse zu schreiben in der die Verbindung läuft.

    In meinem Buch war ein Kapitel das sich mit OOP befasst hat aber das kam mir auf den ersten blick komplizirt rüber somit habe ich es Übersprungen aber wenn es sinnvoll ist werde ich nachdem ich das jetzige Kapitel fertig habe mal zurückspringen und das OOP Kapitel durchnehmen

    Patrick1993 schrieb:

    Ich werde mich dann gleich mal hinter klemmen und ein wenig "Spielen" (wenn man es so sagen darf) und mir die "Methoden etc" mal anschauen und mal "Experimentieren" wie ich den Fehler behoben bekomme.


    Genauso ... dauert zwar länger, aber wenn Du es dann raus hast, dann verstehst Du es auch wirklich.


    Wenn ich den Code also die Verbindung in eine Sub New () packe und dort die Verbindung via Dim y AS New MySQLConnection = "Hier die Verbindungsdaten" speichern tu sollte es doch im Endeffekt reichen ohne ständig den Code abzuändern wenn änderungen an der Datenbank kommen oder liege ich falsch ??

    Soviel ich bis jetzt gelesen habe were es wirklich besser in meiner Anwendung eine Klasse zu schreiben in der die Verbindung läuft.


    Richtig, die Verbindung in einer eigenen Klasse kapseln (oder besser gesagt in einem eigenen Object). Aber was hat der Konstruktor der Klasse (die Sub New()) damit zu tun? Macht es Sinn gleich bei Instanzierung der Klasse bereits die Verbindung zu erstellen oder wäre es sinnvoller die Verbindung erst über den expliziten Aufruf einer Klassen-Methode bei Bedarf zu erstellen? Oder wäre es sinnvoll die Erstellung der Verbindung in eine Methode der Klasse auszulagern und bei Instanzierung der Klasse die Methode anzuspringen ... gleicher Effekt, aber Du könntest bei Bedarf die Verbindung per Methode neu erstellen (z.B. wenn man innerhalb der Sitzung ein Abbruch vorkommen sollte).

    Welche Methoden sollte denn eigentlich so eine Verbindungsklasse beherrschen? Wäre es sinnvoll eine Eigenschaft einzubauen über die man abfragen kann ob die Verbindung überhaupt besteht? Wäre es sinnvoll ein Error-Modell zu implementieren an Hand dessen man Abfragen kann welche Gründe es gibt wieso die Verbindung nicht aktiv ist? Wäre es sinnvoll eine Behandlungsroutine einzubauen die fehlgeschlagene Verbindungsversuche versucht selbstständig zu beheben?

    Alles so Kleinigkeiten die man sich mal fragen sollte wenn man so eine Verbindungsklasse erstellt. ;)


    In meinem Buch war ein Kapitel das sich mit OOP befasst hat aber das kam mir auf den ersten blick komplizirt rüber somit habe ich es Übersprungen aber wenn es sinnvoll ist werde ich nachdem ich das jetzige Kapitel fertig habe mal zurückspringen und das OOP Kapitel durchnehmen


    Das wäre mehr als sinnvoll. Vor allem exorbitant wichtig um VB.NET überhaupt zu verstehen, denn alles was in VB.NET angeboten wird basiert auf OOP.

    Wenn Du OOP verstehst und anwenden kannst, dann hast Du auch von Haus auf wesentlich weniger Probleme Objektmodell in VB.NET zu verstehen und kannst die daher wesentlich besser/sicherer/schneller anwenden ... vor allem ohne jedes Mal nachzulesen/nachzufragen und das spart richtig viel Zeit und richtig viel Nerven.

    Gruß

    Rainer

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

    Ich habe mich mal rangeklemmt und mir hier im Forum einige Threads durchgelesen,
    Mir ist in einem aufgefallen das jemand unter dem Befehl

    SQL-Abfrage

    1. con.CommandText="insert * into Tabellenname"

    einen Reader platziert hat kann es vielleicht daran liegen das ich in dem Code die make habe ?

    Die Methoden das sind doch die "Funtionen" die als Untermenü gelistet werden oder habe ich das falsch aufgefasst ?

    Patrick1993 schrieb:

    Ich habe mich mal rangeklemmt und mir hier im Forum einige Threads durchgelesen,
    Mir ist in einem aufgefallen das jemand unter dem Befehl

    SQL-Abfrage

    1. con.CommandText="insert * into Tabellenname"

    einen Reader platziert hat kann es vielleicht daran liegen das ich in dem Code die make habe ?


    Ein Reader-Object ist normalerweise das Object das die Rückgabe eines Command-Objects aufnimmt. Was für eine Rückgabe erwartest Du denn bei einem INSERT-Befehl? ;)

    Was anderes wäre es bei einem SELECT-Befehl, den man ja ausführt um etwas von der Datenbank zurück zu erhalten. Da wird ein Reader dann nützlich.

    Und jetzt guckst Du Dir mit der Information nochmal die Execute-Möglichkeiten des Command-Objectes an und jetzt müssten Dir die Unterschiede sofort ins Auge springen. ^^


    Die Methoden das sind doch die "Funtionen" die als Untermenü gelistet werden oder habe ich das falsch aufgefasst ?


    Genau richtig. Die Terminologie Methoden einer Klasse bezeichnen immer die von aussen ZUGÄNGLICHEN Subs/Functions in der Klasse (Functions/subs mit Private sind ja nicht von aussen zugänglich).

    Und auch nur diese werden dann im IntelliSense Menü zur Auswahl angezeigt.

    Gruß

    Rainer
    Da ich gerade in der "Aufbauphase" bin will ich vorerst mal einen Eintrag in die Datenbank auf die reihe bekommen.

    Zb. Programm 1 Sendet einen Text (oder was anderes) in die Datenbank und Programm 2 liest es dann aus und gibt es in einem Label,in einer Richtextbox oder in einer Normalen Textbox aus

    Patrick1993 schrieb:



    Zb. Programm 1 Sendet einen Text (oder was anderes) in die Datenbank und Programm 2 liest es dann aus und gibt es in einem Label,in einer Richtextbox oder in einer Normalen Textbox aus


    Da Funktion 1 und 2 beide eine Verbindung zur Datenbank brauchen, ist natürlich der erste Weg eine Klasse/ein Object zu erstellen das die Datenverbindung erstellt und hält.

    Guck dir mal folgenden Ansatz an:

    Klasse Datenbank-Verbindung | MustInherit |
    - Methode Connection: Erstellt Verbindungsaufbau
    - Private Class-Member m_Cnn: Hält die Referenz auf das Verbindungsobjekt
    - ReadOnly Property Cnn: Stellt die Referenz m_Cnn für Zugriffe von aussen zur Verfügung
    - Methode CloseCnn: Schliesst die Verbindung die in m_Cnn gehalten wird mit Close, zerstört das Handling mit Dispose und setzt die referenz m_Cnn auf Nothing

    Klasse Datenoperationen
    * erbt von Klasse Datenbank-Verbindung (Inherits)
    - Constructor: Ruft Methode Connection in MyBase (Basis-Klasse, also in dem Fall die Klasse Datenbank-Verbindung) auf
    - Methode Read: liest aus der Datenbank
    -- erwartet als Übergabe SQL-Statement als String
    -- Return: ? ... vllt ein Reader-Object?
    - Methode Insert: schreibt NEUEN Datensatz in die Datenbank
    -- erwartet als Übergabe SQL-Statement als String
    -- Return: boolscher Wert, True wenn Insert-Vorgang erfolgreich
    - Methode Update: ändert einen vorhandenen Datensatz
    -- erwartet als Übergabe SQL-Statement als String
    -- Return: boolscher Wert, True wenn Update-Vorgang erfolgreich

    Das wäre jetzt mal ein sehr einfaches OOP-Modell um das was Du vorhast über Objekte zu realisieren.

    Dadurch brauchst Du in Deinen Funktionen 1 und 2 eigentlich nichts anderes mehr zu machen als das SQL-Statement zu erstellen und dieses dann von der passenden Methode ausführen zu lassen. Funktion 2 kannst Du natürlich erst erstellen wenn Du eine Entscheidung getroffen hast in welcher Form die Methode Read die gelesenen Daten zurück gibt.

    Probiere Dich mal dran ... ist eigentlich überhaupt nicht schwer. Aber ich denke wenn Du das hinbekommen hast dann hast Du eine ganze Menge zum Thema OOP begriffen. ;)

    Gruß

    Rainer
    Als Classe kann ich da auch eine php Datei nutzen ??
    Also wenn ich eine php Datei schreibe die sich z.b. config.php nennt in der die Datenbankdaten stehen, dann könnte ich doch den Pfad in die Classe eintragen.

    ReadOnly Property Cnn: Stellt die Referenz m_Cnn für Zugriffe von aussen zur Verfügung

    Da verstehe ich gerade Bahnhof Gleis 6.

    - Constructor: Ruft Methode Connection in MyBase (Basis-Klasse, also in dem Fall die Klasse Datenbank-Verbindung) auf

    Der gerieft auf die Verbindung zu also z.b. php Datei ??

    Patrick1993 schrieb:

    Als Classe kann ich da auch eine php Datei nutzen ??
    Also wenn ich eine php Datei schreibe die sich z.b. config.php nennt in der die Datenbankdaten stehen, dann könnte ich doch den Pfad in die Classe eintragen.


    Das ist das Schöne bei objektorientierter Programmierung ... was im Objekt selber passiert ist völlig egal. Hauptsache die darauf zugreifenden Klassen oder die davon erbenden Klassen bekommen das was sie erwarten: Ein Connection-Object das eine aktive Verbindung zur Datenbank beinhaltet. wie das zu Stande kommt und was da alles im Object passiert ist denen anderen Klassen absolut egal.

    Ob Du das über eine PHP-Datei hinbekommst ... keine Ahnung, habe ich noch nie probiert und kenne mich auch mit PHP zu wenig aus.


    ReadOnly Property Cnn: Stellt die Referenz m_Cnn für Zugriffe von aussen zur Verfügung

    Da verstehe ich gerade Bahnhof Gleis 6.


    Kurz:

    Dim Zahl As Integer = 8

    Deklariert eine Variable vom Typ Wertvariable. Der wird der Wert 8 zugewiesen und alles was auf die Variable zugreift bekommt auch den Wert 8.

    Dim MyKlasse As New Klasse

    Deklariert eine Variable vom Typ Verweis-Variable. Denn eine instantierte Klasse/ein instanziertes Object hat in dem Sinne keinen Wert. Daher wird in der Variable nur ein Verweis/eine Referenz auf die Speicheradresse gehalten und alles was auf die Variable zugreift bekommt die Referenz auf den Speicher mitgeteilt.

    Properties sind die von aussen erreichbaren Eigenschaften einer Klasse, in den meisten Fällen ermöglich sie den Zugriff auf die privaten Klassen-Member ... lesend/schreibend oder nur lesend.

    Ein Klassen-Member ist i.d.R. nix anderes als eine Variable die in der Klasse deklariert wurde und nicht in einer Function/Sub. Solche Variablen die klassenweit gültig sind deklariert man i.d.R. mit Private. Damit können Sie von aussen weder gelesen noch geschrieben werden.

    Besteht nun die Notwendigkeit so einen Klassen-Member nach aussen hin zur Verfügung zu stellen, regelt man das über eine Property. ReadOnly in dem Fall damit die Property von aussen nur gelesen werden kann, aber nicht geschrieben werden kann ... die Verbindung aufzubauen soll ja im Object selber geschehen.

    VB.NET-Quellcode

    1. Public MustInherit Class MyDBConnection
    2. Protected Sub New()
    3. End Sub
    4. Private m_Cnn As MySqlConnection = Nothing
    5. Public ReadOnly Property Cnn As MySQLConnection
    6. Get
    7. Return m_Cnn
    8. End Get
    9. End Property
    10. Protected Sub SetDBConnection()
    11. m_Cnn = New MySQLConnection
    12. With m_Cnn
    13. .ConnectionString = ("server=81.20.134.98; uid=text; password=++++; database=test;")
    14. .Open()
    15. End With
    16. End Sub
    17. End Class


    Jetzt bauste mal eine Klasse die die obige Klasse beerbt und bei der Initialisierung (z.B. im Konstruktor) beinhaltet das SetDBConnection aus der Basis-Klasse (also die Klasse von der geerbt wird) aufruft. Und dann guckste mal was Dir zu der obigen Klassen in IntelliSense angeboten wird ... da findest Du SetDBConnection und Cnn.

    Dann sprichst Du Deine selbstgebaute Klasse mal woanders an mit einer einfachen Referenzierung in einer sub (Dim Klasse As New DeinKlassenName). Und dann guckst Du was Dir dort angeboten wird über IntelliSense ... und Du wirst entdecken das Dir Cnn (aber nur Cnn wegen Public, SetDBConnection nicht mehr wegen Protected) auch dort angeboten wird. Und deswegen kannst nun überall dank der öffentlichen Property Cnn auf das Klassen-Member m_Cnn das das Connection-Object referenziert zugreifen kannst.


    - Constructor: Ruft Methode Connection in MyBase (Basis-Klasse, also in dem Fall die Klasse Datenbank-Verbindung) auf

    Der gerieft auf die Verbindung zu also z.b. php Datei ??


    Genau. Siehe oben, Du musst es dort nur schaffen über die/mit Hilfe der PHP-Datei eine gültige Verbindung an m_Cnn zu zu weisen. Dann wird an jeder Stelle in Deinem Programm das die Child-klasse referenziert (Child ist die Klasse die von der Klasse MyDBConnection erbt) über die Property Cnn immer nur auf diese Verbindung zu gegriffen.

    Alles klar? :D

    Gruß

    Rainer
    Ich habe mir ein Moodul geschrieben in der die ganzen Zugriffsdaten vorhanden sind.
    Dieses Modul sieht so aus:

    VB.NET-Quellcode

    1. Imports MySql.Data.MySqlClient
    2. Module Module1
    3. Public con As New MySqlConnection
    4. Public cmd As New MySqlCommand
    5. Public reader As MySqlDataReader
    6. End Module


    Dann ist im meinem eigendlichen Programm der Code als neue Public Sub mit den ganzen Daten (Server,User etc) der Code sieht so aus:

    VB.NET-Quellcode

    1. Imports MySql.Data.MySqlClient
    2. Public Class Form1
    3. Private Sub Verbindung()
    4. Dim con As New MySqlConnection
    5. Dim cmd As New MySqlCommand
    6. Dim reader As MySqlDataReader
    7. con.ConnectionString = ("server=81.20.134.98; uid=test; password=xxx; database=test;")
    8. cmd.Connection = con
    9. End Sub


    Ist dieser Code

    VB.NET-Quellcode

    1. If txt_name.Text = "" Or txt_alter.Text = "" Or txt_email.Text = "" Then
    2. msgbox("Füllen Sie alle Felder aus!")
    3. Else
    4. Try
    5. con.Open() 'Verbindung zur Db öffnen
    6. cmd.CommandText = "INSERT INTO Benutzer(Name, Alter, EMail) VALUES ('" & txt_name.text & "', '" & txt_alter.text & "', '" & txt_name.Text & "')" 'Der Befehl für die DB
    7. anzahl = cmd.ExecuteNonQuery 'anzahl enthält nun ein Wert alle geänderten/ hinzugefügten/ gelöschten Einträge
    8. con.close 'Verbindung zur DB schließen
    9. If anzahl > 0 Then 'Nun wird kontrolliert ob überhaupt ein Eintrag hinzugefügt geworden ist, wenn ja dann die MSG
    10. MsgBox("Sie haben einen Eintrag gemacht", MsgBoxStyle.Information)
    11. End If
    12. Catch ex As Exception
    13. con.close 'ich schließe hier ebenfalls die Verbindung, weil wenn ein Fehler in dem oberen code passiert, passiert er vor dem schließen der Verbindung. Wenn ich das nicht machen würde käme der Fehler das die Verbindung noch offen ist, wenn ich das nächste mal eine Verbindung öffne.
    14. MsgBox(ex.Message)
    15. End Try
    16. End If


    Immer gleich (abgesehen von dem Inhalt der befehle) ??

    Ich hoffe ich habs langsam geschluckt :D
    Der oben stehende Code ist aus einem Tutorial hier im Forum. Ich habe gefragt ob er immer gleich ist da soviel ich bis jetzt gesehen habe sehr oft verwendet wird

    Patrick1993 schrieb:

    Ich habe mir ein Moodul geschrieben in der die ganzen Zugriffsdaten vorhanden sind.
    Dieses Modul sieht so aus:

    VB.NET-Quellcode

    1. Imports MySql.Data.MySqlClient
    2. Module Module1
    3. Public con As New MySqlConnection
    4. Public cmd As New MySqlCommand
    5. Public reader As MySqlDataReader
    6. End Module


    Pfui, pfui, pfui ... ;)

    Das ist alter VB6/VBA-Stil, aber für eine DB-Connection hätte man das selbst in den beiden Sprachen so nie gemacht. ^^

    Aber in VB.NET sollte man sowas definitiv nicht mehr machen, vor allem völlig unnötig und extrem gefährlich, Stichwort Multi-Threading ... geht völlig in die Hose. Anstatt auf die öffentlichen Vars zu zu greifen solltest Du ja über Objecte auf sowas zugreifen.

    Es könnte ja durchaus möglich sein das Du verschiedene Arten von Verbindungen halten willst. Dafür ist dann eine öffentliche Variabel völlig ungeeignet das sie nur 1 Verbidung speichern kann und eine neue Verbindung die alte überschreiben würde und das führt dann i.d.R. zu völligem Datenchaos und Datenverlust.

    Öffentliche Variablen können immer nur 1 Zustand speichern (also Wert/Verweis halten), z.B. Datenabgleiche zwischen 2 verschiedenen Datensätzen wären mit dem Modell der öffentlichen Variablen überhaupt nicht möglich weil Du eben keine zwei verschiedenen Records speichern kannst. Hättest Du das aber alles in Klassen gemacht dann wäre es problemlos möglich da Du ja soviele Instanzen einer Klasse wie Du willst parallel zueinander erstellen und handeln kannst. Deswegen ist Multi-Threading auch nur mit Objecten sinnvoll umsetzbar. ;)

    Siehe Dir oben meinen Beispielcode an ... das ist das Grobprinzip einer Verbindungsklasse und kannst du direkt so einbauen. Wenn Du das MustInherit rausnimmst und die beiden Protected in Public umwandelst hättest Du ein Objekt das Du an jeder x-beliebigen Stelle im Code ansprechen und nutzen kannst (natürlich etwas mehr Code-Aufwand als bei der Ansprache einer Public Var ... aber dafür gibt es auch Programmiermethoden wo Du Objekte genauso wie Public Variablen ansprechen kannst Stichwort Interfaces und Extensions, aber das zeige ich Dir wenn Du den Teil hier erstmal richtig umgesetzt hast).

    Ich habe nur die MustInherit-Version gewählt, weil in den meisten Fällen die Connection alleine nicht hilft, sondern eben Daten-Operationen dazu benötigt werden. Die sind meistens auch allesamt identisch ... SELECT, INSERT, UPDATE, CREATE und ALTER sind wohl die Anweisungen mit denen man zu 99% arbeitet und wenn man 5 Arten von Operationen für 99% aller Fälle benötigt schreit das geradezu nach einer Klasse für Datenoperationen.


    Dann ist im meinem eigendlichen Programm der Code als neue Public Sub mit den ganzen Daten (Server,User etc) der Code sieht so aus:

    VB.NET-Quellcode

    1. Imports MySql.Data.MySqlClient
    2. Public Class Form1
    3. Private Sub Verbindung()
    4. Dim con As New MySqlConnection
    5. Dim cmd As New MySqlCommand
    6. Dim reader As MySqlDataReader
    7. con.ConnectionString = ("server=81.20.134.98; uid=test; password=xxx; database=test;")
    8. cmd.Connection = con
    9. End Sub


    Part One ... wieso deklarierst Du in der Sub die Variablen nochmal neu wenn Du doch eigentlich über die öffentlichen Variablen gehen wolltest? ;)

    Part Two ... genau das sollte aber verhindert werden mit meinem Vorschlag, dass Du jedes Mal im Code die Verbindung erstellst, sondern die Verbindung soll in einer eigenen Klasse erstellt werden und von dieser auch gehalten werden in einer Variable, diese kann dann über die Property ausgelesen werden.


    Ist dieser Code

    VB.NET-Quellcode

    1. If txt_name.Text = "" Or txt_alter.Text = "" Or txt_email.Text = "" Then
    2. msgbox("Füllen Sie alle Felder aus!")
    3. Else
    4. Try
    5. con.Open() 'Verbindung zur Db öffnen
    6. cmd.CommandText = "INSERT INTO Benutzer(Name, Alter, EMail) VALUES ('" & txt_name.text & "', '" & txt_alter.text & "', '" & txt_name.Text & "')" 'Der Befehl für die DB
    7. anzahl = cmd.ExecuteNonQuery 'anzahl enthält nun ein Wert alle geänderten/ hinzugefügten/ gelöschten Einträge
    8. con.close 'Verbindung zur DB schließen
    9. If anzahl > 0 Then 'Nun wird kontrolliert ob überhaupt ein Eintrag hinzugefügt geworden ist, wenn ja dann die MSG
    10. MsgBox("Sie haben einen Eintrag gemacht", MsgBoxStyle.Information)
    11. End If
    12. Catch ex As Exception
    13. con.close 'ich schließe hier ebenfalls die Verbindung, weil wenn ein Fehler in dem oberen code passiert, passiert er vor dem schließen der Verbindung. Wenn ich das nicht machen würde käme der Fehler das die Verbindung noch offen ist, wenn ich das nächste mal eine Verbindung öffne.
    14. MsgBox(ex.Message)
    15. End Try
    16. End If


    Immer gleich (abgesehen von dem Inhalt der befehle) ??


    Beantworte ich Dir wenn Du mir Zeile für Zeile beschreibst was dort passiert und wieso es dort so passiert. :D


    Ich hoffe ich habs langsam geschluckt :D


    Oh ... ich glaube da scheint es noch viel Arbeit zu geben bis es soweit ist. ^^

    Gruß

    Rainer
    Ich habe deinen Obrigen Code (Classe) mal genutzt.
    Der Code sieht nun so aus:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports MySql
    2. Imports MySql.Data
    3. Imports MySql.Data.MySqlClient
    4. Public Class Form1
    5. Public Class MyDBConnection
    6. Public Sub New()
    7. End Sub
    8. Private con As MySqlConnection = Nothing
    9. Public ReadOnly Property Cnn As MySQLConnection
    10. Get
    11. Return con
    12. End Get
    13. End Property
    14. Public Sub SetDBConnection()
    15. con = New MySqlConnection
    16. With con
    17. .ConnectionString = ("server=81.20.134.98; uid=text; password=++++; database=test;")
    18. .Open()
    19. End With
    20. End Sub
    21. End Class
    22. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    23. Dim cmd As New MySqlCommand
    24. Dim con As New MyDBConnection
    25. con.Cnn.Open()
    26. cmd.CommandText = ("SELECT * FROM test")
    27. End Sub
    28. End Class



    Hoffe das es so richtig ist um weiter zu Experimentieren

    Patrick1993 schrieb:

    Ich habe deinen Obrigen Code (Classe) mal genutzt.
    Der Code sieht nun so aus:
    ...

    Hoffe das es so richtig ist um weiter zu Experimentieren


    Nö ... falsch umgesetzt ... Du hast die Connection-Class als Sub-Class für die Form erstellt (einfach weil die Klasse sich innerhalb des Gültigkeitsbereiches der Klasse Form1 befindet). ^^

    Zuerst legst Du ein neues Klassenmodul an, dann sieht mein Code so in etwa aus:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports MySql
    2. Imports MySql.Data
    3. Imports MySql.Data.MySqlClient
    4. Namespace DBHandling
    5. Public Class MyDBConnection
    6. Public Sub New()
    7. End Sub
    8. Private m_Con As MySqlConnection = Nothing
    9. Public ReadOnly Property Cnn As MySQLConnection
    10. Get
    11. Return m_Con
    12. End Get
    13. End Property
    14. Public Sub OpenDBConnection()
    15. m_Con = New MySqlConnection
    16. With m_Con
    17. .ConnectionString = ("server=81.20.134.98; uid=text; password=++++; database=test;")
    18. .Open()
    19. End With
    20. End Sub
    21. Public Sub CloseDBConnection
    22. With m_Con
    23. .Close()
    24. '// wird Dispose als Methode angeboten? Wenn ja, dann auch dringend ausführen
    25. .Dispose()
    26. End With
    27. con = Nothing
    28. End Sub
    29. End Class
    30. End Namespace


    Damit es wieder was zu Denken gibt, habe ich gleich mal einen Namensraum mit eingebaut (Namespace) und natürlich noch die Methode zum Schliessen der Verbindung. ;)

    Danach gehst Du in die Klasse zur Form und dort sieht das dann so aus:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    3. '// Connection-Class deklarieren und initialisieren
    4. Dim con As New DBHandling.MyDBConnection
    5. '// Command-Object deklarieren und initialisieren
    6. Dim cmd As New MySqlCommand
    7. '// Verbindung in der Connection-Class erstellen lassen
    8. con.OpenDBConnection()
    9. '// Erstellt Verbindung dem Command-Object zuweisen durch Ansprache der Property Cnn
    10. cmd.Connection = con.Cnn
    11. '// SQL-Statement der Property CommandText des Command-Objects zuweisen
    12. cmd.CommandText = ("SELECT * FROM test")
    13. '// hier fehlt nocht die Ausführung des cmd-Objectes, also ein Execute mit Rückgabe ... z.B. an ein Reader Object?
    14. '// nach Ausführung Connection in der Connection-Class schliessen
    15. con.CloseDBConnection()
    16. '// Referenz/Verweis auf Connection-Class wieder zerstören
    17. con = Nothing
    18. End Sub
    19. End Class


    Du musst also in der Sub Button1_Click noch die Ausführung des Command-Objektes ergänzen.

    Noch ein paar Anmerkungen:

    1. Private Member einer Klasse kennzeichnet man besonders, i.d.R. durch entweder das Präfix "_" oder durch das Präfix "m_". Einfach damit Du immer sofort weisst woher die Variable stammt wenn Du mal Code liest den Du vor längerer Zeit geschrieben hast.

    2. Groß-/Kleinschreibung bei der Variablen-Benamsung ist keine Macke sondern eine ältere Norm (Kamelschreibweise, weil die großen Buchstbane wie ein Höcker wirken). Okay, ist in VB.NET nicht mehr so dringend nötig. Aber man hat es früher gemacht um gleich zu erkennen ob man eine Variable falsch geschrieben hat (Schreibfehler sollen ja vorkommen). Man hat die Variable bei der Deklaration mit Kamelschreibweise versehen und wenn man sie im Code geschrieben hat, dann hat man sie nur in Kleinbuchstaben geschrieben. Wurde die Variable nach dem betätigen der Enter-Taste nicht automatisch mit "Höckern" versehen, wusste man sofort das es die Variable nicht gibt weil man entweder Deklaration vergessen hat oder schlicht falsch geschrieben hat. ^^

    Aber gut ... in VB.NEt braucht man das heute nicht mehr wirklich wenn man die passenden Options angeschaltet hat.

    3. Und wie Du oben im zweiten Code siehst, liest sich ein Code viiiiel besser wenn er kommentiert ist. Daher sollte man seinen Code immer mit Kommentaren versehen, natürlich nicht ganz so detailliert wie oben. Aber glaub mir, wenn Du in ein paar Monaten Code anguckst den Du heute geschrieben hast, wirst Du dankbar sein wenn Du ihn kommentiert hast und Du nicht erst lange überlegen musst "Verdammt, wieso habe ich das so gemacht und was soll das eigentlich.". Das wird Dir öfters passieren als Du Dir jetzt vorstellen kannst. Also: Immer kommentieren ... auch bei Tests, bzw. gerade bei Tests. ;)

    All das mag dir jetzt kleinlich und pingelig erscheinen, aber glaub mir eines wenn Du mal mehr programmierst wirst Du ziemlich schnell feststellen das Dir das alles extrem hilft und Du damit Probleme und Ärger ohne Ende vermeidest. ;)

    Gruß

    Rainer
    Wen ich es richtig verstanden habe muss ich ein Klassenmodul mit z.b. deinem Code erstellen das dann quasi mit der Form1 verbunden wird.

    Und dann einfach in dem Button 1 die Klasse ansprechen, die ich laut deinem Code mit

    VB.NET-Quellcode

    1. Dim con As New DBHandling.MyDBConnection

    mache (Warum DBHandlind ??)
    .
    Dann die cmd Definieren mit

    VB.NET-Quellcode

    1. Dim cmd As New MySqlCommand

    Und dann einfach nur die Verbindung öffnen und einfach die cmd Befele schreiben.

    Wofür ist das Dspose in deiner Classe gut ??
    Ich hab mal davon gehört habe aber noch nicht so ganz kapiert wofür es ist

    Patrick1993 schrieb:

    Wen ich es richtig verstanden habe muss ich ein Klassenmodul mit z.b. deinem Code erstellen das dann quasi mit der Form1 verbunden wird.


    So in etwa richtig. Bis auf die Formulierung "Form1 verbunden wird".

    Eine Klasse ist etwas komplett Eigenständiges und bildet das was man im OOP ein Object nennt. Um eine Klasse/ein Object zu nutzen reicht es aus die Klasse zu deklarieren und mit dem Schlüsselwort New eine neue Instanz der Klasse zu erstellen. Überall wo Du das machst kannst Du dann alle Eigenschaften/Porperties und Methoden der Klassen nutzen.

    Die Klasse die ich da mal im ganz im Groben erstellt habe ist exakt das gleich wie das Command-Object was Du ansprichst. Das Command-Object ist nämlich auch nix anderes als eine Klasse. Und auf dem gleichen Wege wie Du in Deinem Code bis jetzt ein Command-Object deklariert hast und nutzt, kannst Du auch dann Deine eigenen Klassen überall deklarieren und nutzen.

    Jetzt klar wieso ich mal einige Posts zuvor gesagt habe, wenn Du die Erstellung und Nutzung eigener Klasse verstanden hast wirst Du auch alle Objekt-Modelle in VB.NET wesentlich besser verstehen ... denn ein Object ist genauso eine Klasse wie wir sie hier gerade für die Datenbank-Connection zusammen erstellen. ;)


    Und dann einfach in dem Button 1 die Klasse ansprechen, die ich laut deinem Code mit

    VB.NET-Quellcode

    1. Dim con As New DBHandling.MyDBConnection

    mache ...


    Richtig formuliert und deswegen auch der Hinweis zuvor das die Klasse nicht mit der Form verbunden sind sondern schlicht dort wo du sie brauchst "angesprochen" wird.


    (Warum DBHandlind ??)


    Guck Dir mal den Namespace an ... den habe ich DBHandling genannt. Und alles was sich an Klassen, Structures, Interfaces u.ä. innerhalb dieser Deklaration des Namespaces befindet wird eben mit NamespaceName.KlassenName angesprochen.

    Der Namespace ist ein reines Ordnungskonzept. Alle klassen/Objecte die Du zu dem Thema Umgang mit der DB erstellst packst Du innerhalb des Namespaces rein (Die Nutzung von Partial bei der Namespace Ordnung erkläre ich Dir mal später ;) ) und hast dann den riesen Vorteil das Dir IntelliSense genau anzeigt was Du in dem Namespace zur Verfügung hast. Du musst Dir also nicht mehr die einzelnen Namen von Klassen, Interfaces, Structures o.ä. merken, sondern nur noch in welchem Namespace Du das organisiert hast und nach der Codeeingabe 'Namespace. ' zeigt Dir IntelliSense nur noch die Sachen an die in dem Namespace vorhanden sind.

    Bei einer App mit 3-4 Klasse wird Dir der sinn dahinter nicht einleuchten können. Aber eine mittlere App kann schon problemlos über 1.000 Klassen kommen ... und dann suchst Du Dir einen Affen wie habe ich nun die Klasse XYZ genannt damit ich sie hier im Code ansprechen kann. Deswegen organisiert man in Namensräumen und findet dadurch seine Sachen viiiiiel leichter wieder.

    Die meisten Objekt-Modell in VB.NEt sind in Namespaces organisiert. Z.B. System.Data.MySqlCommand ... Projekt System (Name der Dll), Namespace Data, Klasse MySqlCommand. Alles klar? ;)
    .

    Dann die cmd Definieren mit

    VB.NET-Quellcode

    1. Dim cmd As New MySqlCommand

    Und dann einfach nur die Verbindung öffnen und einfach die cmd Befele schreiben.


    So richtig ... aber natürlich kann die Verbindung so nur über die Klassen-Methode OpenDBConnection geöffnet werden und auch nur über CloseDBConnection geschlossen werden. Die Property Cnn stellt den Lese-Zugriff auf die Connection-Reference da und deswegen kannst Du die dann dem Command-Object als zu nutzende Command.Connection zu weisen.

    Und denk mal an alle Fälle wo du ein Command-Object brauchen würdest ... in alle diesen Fällen nutzt Du nur noch diese Klasse genauso wie Du sie hier nutzt.


    Wofür ist das Dspose in deiner Classe gut ??
    Ich hab mal davon gehört habe aber noch nicht so ganz kapiert wofür es ist


    Dispose = Entlassen.

    Jedes Object (also Klasse ... aber mittlerweilen klar, oder?) das eine Methode Dispose anbietet hat dort Code der nix anderes tut als dafür zu sorgen das ordentlich aufgeräumt wird und das Object auch tatsächlich frei gegeben (entlassen) wird.

    Und daher heisst die Regel, wenn eine Methode Dispose angeboten wird, dann nutze Sie auch immer um das Objekt zu entlassen wenn Du es im Code nicht mehr brauchst.

    Übrigens ... für Objects die eine Dispose-Methode beinhalten kannst Du auch die Using-Methode alternativ zur klassichen Deklaration verwenden, also so:

    VB.NET-Quellcode

    1. Dim XYZ As New Namespace.KlasseXYZ
    2. '// tu was mit der Klasse
    3. XYZ.Dispose()
    4. XYZ = Nothing
    5. '// gleicher Effekt bloss Verwendung von Using:
    6. Using XYZ As New Namespace.KlasseXYZ
    7. '// tu was mit der Klasse
    8. End Using


    Bei "End Using" wird dann dafür gesorgt das die Dispose-Methode des Objects ausgeführt wird und die deklarierte Variable (in beiden Fällen ja XYZ) auch zurück gestetzt wird.

    Man tut sich damit halt etwas einfacher weil man nicht immer selber das Entlassen des Objects programmieren muss, sondern eben standardmäßig End Using angibt.

    Die Methode Dispose() kannst Du jeder Klasse durch die Implementierung der Schnittstelle IDisposeable (Implements IDisposable) hinzufügen und dann kannst Du auch selber erstellte Klasse per Using ansprechen.

    Das aber nur am Rande.

    Gruß

    Rainer
    ok nun bin ich wieder an der reihe :D
    Ich werde mich vielleicht heute mal hinpflanzen und alles was du mir hier so nett erklärt hast (Ein Riesen Dankeschön dafür) mal versuchen umzusetzen.
    Nun bin ich dank dir um einiges schlauer da all dies was du mir so nett erklärt hast nicht in meinem Buch steht.

    Patrick1993 schrieb:

    ok nun bin ich wieder an der reihe :D


    Das sehe ich auch so. :D


    Ich werde mich vielleicht heute mal hinpflanzen und alles was du mir hier so nett erklärt hast (Ein Riesen Dankeschön dafür) mal versuchen umzusetzen.
    Nun bin ich dank dir um einiges schlauer da all dies was du mir so nett erklärt hast nicht in meinem Buch steht.


    Gern geschehen und offensichtlich scheint ja schon ein Teilziel teilweise erreicht zu sein (besseres Verständnis). ;)

    Das steht schon irgendwo alles in den meisten Büchern, bloss meistens als Randnotiz oder an einer Stelle wo man es gar nicht sucht/vermutet. Manchmal habe ich das Gefühl das die ganzen Leute die Bücher für Einsteiger schreiben irgendwie völlig vergessen haben was Einsteiger wirklich für Probleme haben, bzw. was man wirklich zuerst beibringen sollte.

    Gruß

    Rainer