Abfragen: existiert der MySqlDataReader

  • VB.NET

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

    Abfragen: existiert der MySqlDataReader

    Hallo ihr Lieben,
    der MySqlDataReader macht mir immer ein bißchen Probleme.

    kann ich irgendwie abfragen, ob der MySqlDataReader überhaupt offen ist ?

    bei der Verbindung zur Mysql-Db ist das ganz einfach, z.B. so:

    VB.NET-Quellcode

    1. If con.State.ToString() = "Closed" Then
    2. con.Open()
    3. End If


    d.h. die Verbindung hat einen Status.

    Gibt es sowas auch für einen MySqlDataReader ?

    Ich bin mir nicht sicher, ob ich mich da irgendwie in einem Problem verrannt haben, dass ganz anders gelöst werden muß, aber immer wenn ich den DataReader abfrage und er aus irgendwechen Gründen nicht exisitiert, also nicht vorher geöffnet wurde, dann gibt es eine Fehlermeldung. Das ist irgendwie unbefriedigend.

    lieben Gruß
    Gabi
    in der Tat, "offen" ist das falsche Wort, ich habe das nur gewählt, weil man ja auch den DataReader mit Close schließt....

    Ich muß mir mal die Exceptions genauer anschauen, vielleicht löst das mein Problem mit den gelegentlichen Fehlermeldungen.
    also ich habe folgendes gemacht...

    Wenn ich einen Datensatz in eine Tabelle eintrage, dann soll aus der zugehörigen Datensatznummer noch ein kleiner Wirrwar-Kode gebildet werden, also so ein paar Zahlen und Buchstaben, dann die Datensatznummer und wieder ein paar Zahlen und Buchstaben.
    Damit ist natürlich dieser Kode eindeutig.

    Also habe ich nach dem Insert eine Abfagen nachgeschoben, sowas in der Art

    VB.NET-Quellcode

    1. sql_befehl = ""
    2. sql_befehl = "SELECT LAST_INSERT_ID()"
    3. cmd.CommandText = sql_befehl
    4. myData = cmd.ExecuteReader()
    5. Do While myData.Read()
    6. diese_id = CInt(myData("LAST_INSERT_ID()"))
    7. Loop


    beim Eintragen von Datensätzen habe ich nun festgestellt, dass das manchmal, also nicht sehr oft, zu einer Fehlermeldung führt, d.h. konkret ist es die Do While - Schleife (läuft ja nur einmal), wo die letzte ID ausgelesen wird.

    ich denke aber, ich habe es jetzt in den Griff bekommen, und zwar mit einer Abfrage:

    VB.NET-Quellcode

    1. myData = cmd.ExecuteReader()
    2. If (myData IsNot Nothing) Then
    3. Do While myData.Read()
    4. diese_id = CInt(myData("LAST_INSERT_ID()"))
    5. Loop
    6. End If


    Findest du die Lösung blöd oder instabil ?
    Vielleich macht man das auch ganz anders ? Hhhhmmm
    @ weiss_gabi

    Erstens schliesse ich mich picoflop bei den Fragen an.

    Zweitens verstehe ich immer noch nicht wirklich was Du nun genau vor hast und vorallem warum?

    Wenn Du irgendwas tun willst NACHDEM Du einen Datensatz in ein Table eingefügt hast ist die sinnvollste Methode eigentlich ein Trigger in der DB einzurichten.

    Den richtest Du halt einmal ein und musst Dich nie wieder drum kümmern, denn der wird IMMER ausgeführt nachdem dann auf das Table in Insert, Delete oder Update ausgeführt wird (je nachdem für welches Ereignis Du den Trigger einrichtest).

    Gruß

    Rainer
    zur Zeit kann ich den Fehler nicht wieder reproduzieren, und deshalb habe ich die Fehlermeldung nicht zur Hand...
    ich beobachte das mal ein bißchen.
    Diese Trigger-Lösung klingt auch ganz vielversprechend. Das schaue ich mir mal genauer an.
    @Zweitens verstehe ich immer noch nicht wirklich was Du nun genau vor hast und vorallem warum?
    also das ist ganz einfach, wirklich keine dolle Sache, ich erkläre es noch mal eben.

    jeder Datensatz erhält einen, nennen wir ihn mal einen Spezialcode.
    Der Spezialcode besteht aus 3 beliebigen Zeichen oder Buchstaben, dann die Datensatznummer (fünfstellig) und wieder 3 beliebige Zahlen oder Buchstaben, also z.B. abc00015w0r
    beim 15. Datensatz. Dieser Code wird in der DB in einem speziellen Tabellenfeld abgelegt.
    Das ist auch schon alles.

    Meine bisherige Vorgehensweise:
    Datensatz einfügen (Insert Befehl)
    Last_insert_id auslesen (Select Befehl)
    Spezialcode bilden, wie oben beschrieben
    Spezialcode in die Tabelle schreiben (Update Befehl)

    Die DB ist keine locale DB sondern liegt auf einem anderen remoten Server.
    Normalerweise funktioniert meine Vorgehensweise einwandfrei, nur hatte ich plötzlich eine Fehlermeldung, die ich euch gerne präsentiere, sobald ich sie reproduzieren kann.

    Wenn der Fehler auftritt, wird zwar ein neuer Datensatz eingefügt, aber dieser Spezialcode wird nicht eingetragen.
    Deshalb habe ich vermutet, dass der Fehler bei dem Select-Befehl (siehe oben) zu suchen ist, sprich das der MySqlDataReader leer ist und dann einen Ausnahmefehler verursacht, weil versucht wird, etwas aus dem DataReader zu lesen (siehe While - Schleife oben)

    Klingt doch logisch oder ?

    Ich habe das jetzt durch die If-Bedingung abgefangen und vielleicht ist damit ja auch der Drops gelutscht.

    lieben Gruß
    Gabi

    weiss_gabi schrieb:



    Ich habe das jetzt durch die If-Bedingung abgefangen und vielleicht ist damit ja auch der Drops gelutscht.


    Gut, ich verstehe die Vorgehensweise aber nicht den sinn dahinter. Aber gut ... man(n) muss ja nicht alles verstehen. ^^

    Aber nein ... der Drops ist nicht gelutscht. Das was Du machst ist kurz gesagt: Unsauber und mehr als fehleranfällig.

    Wenn dieser Spezialcode wichtig ist, dann gibt es nur eine einzige saubere Lösung: einen Trigger direkt in der DB einzurichten.

    Egal ob die Connection zusammenbricht, der Client abstürzt, eine Exception auftritt oder Du nur an einer Stelle im Code vergisst den Spezialcode einzufügen ... egal was, der Trigger wird immer ausgelöst wenn auf die Tabelle ein Insert, Update oder Delete ausgeführt wird (je nachdem für welches Event Du ihn einrichtest).

    Gruß

    Rainer
    @Gut, ich verstehe die Vorgehensweise aber nicht den sinn dahinter. Aber gut ... man(n) muss ja nicht alles verstehen.

    Wieso ist das so schwer verständlich, dafür gibt es doch ganz einfache Beispiel:
    z.B. wenn jemand beim Anmelden einen Rabattcode bekommt, dann ist es doch naheliegend, diesen Rabattcode irgendwie mit der Datensatznummer zu koppeln.

    @Trigger
    du hast vollkommen recht, die Trigger sind für dieses Problemchen die Mittel der Wahl.
    Man könnte z.B. nach dem INSERT erst mal die Datensatznummer in das Spezialcode-Feld eintragen und dann in einem weiteren Schritt vorne und hinten noch ein paar zufällige Zeichen ergänzen.
    Aber bei den Triggern gibt es auch eine Schwierigkeit. Man braucht eine SuperPrivileg, damit es einem erlaubt ist, solche Trigger zu benutzen.
    Daran scheitert es wohl bei einem "normalen" Webspace, wie man ihn für 5 Euro im Monat mieten kann.

    Also werde ich wohl doch mit meiner "unsauberen" Lösung leben müssen. Schade, die Triggerlösung wäre besser :(

    lieben Gruß
    Gabi

    weiss_gabi schrieb:

    Daran scheitert es wohl bei einem "normalen" Webspace, wie man ihn für 5 Euro im Monat mieten kann.

    Dann mach doch so:

    Beim INSERTEN speichere in dem Feld einen "Hash" (irgendwas eindeutiges jedenfalls). Und danach machste dann ein Update, der den Datensatz anhand dieses Hashes identifiziert und das Feld entsprechend setzt. Also in der Art wie

    UPDATE Tabelle SET spezial = "abc" + tabelle.id + "def" WHERE spezial = "abcdabcd12344321"
    Das ist auch ein interessanter Ansatz, insbesondere weil er ohne LAST_INSERT_ID auskommt, was ja unter Umständen nicht richtig sein muß, weil sich ja ein weiterer INSERT zwischen den ersten INSERT und dem SELECT "drängeln" könnte.

    @drängeln
    Ist doch so oder sehe ich das falsch ?
    @ Gabi_Weiss


    Wieso ist das so schwer verständlich, dafür gibt es doch ganz einfache Beispiel:
    z.B. wenn jemand beim Anmelden einen Rabattcode bekommt, dann ist es doch naheliegend, diesen Rabattcode irgendwie mit der Datensatznummer zu koppeln.


    Genau das ist unverständlich. ^^

    Normalerweise würden Rabattcodes eine eigenes Table haben. Die Zuordnung eines Rabatt-Codes zu einem Kunden erfolgt dann über den Eintrag der Kunden-ID als FK in die Tabelle des Rabattcodes. Dann wäre die Bildung eines Rabattcodes völlig unabhängig von einem Kundeneintrag und so sollte es eigentlich auch sein.

    Somit wäre auch der Fall abgesichert wenn ein Kunde im Laufe seiner "Lebensdauer" bei Dir mal die Möglichkeit auf einen zweiten oder dritten Rabattcode haben sollte (z.B. Aktionen wie "Freunde werben Freunde" oder "Rabatt für den nächsten Einkauf wenn aktueller Einkauf über XXX € liegt" oder solche Geschichten). Bei Deiner Weise kannst Du ja nur einen Rabattcode pro Kunde anlegen oder Du fängst an richtig "unsauber" zu werden und legst Felder wie SpecialCode1, SpecialCode2 etc. an.


    Man könnte z.B. nach dem INSERT erst mal die Datensatznummer in das Spezialcode-Feld eintragen und dann in einem weiteren Schritt vorne und hinten noch ein paar zufällige Zeichen ergänzen.


    Wieso? SQL beherrscht durchaus rudimentäre Textverarbeitung ...

    - MID(KunName, 2, 3) liefert Dir genau 3 Zeichen ab Position 2 aus dem Field KunName
    - LEFT(KunName, 3) liefert Dir die ersten 3 Zeichen von links aus dem Field KunName
    - RIGHT(KunName, 3) liefert Dir die ersten 3 Zeichen von RECHTS aus dem Field KunName


    weil sich ja ein weiterer INSERT zwischen den ersten INSERT und dem SELECT "drängeln" könnte.

    @drängeln
    Ist doch so oder sehe ich das falsch ?


    Nur wenn Du mit der gleichen Connection parallel in unterschiedlichen Threads arbeitest (was aber eh nicht wirklich günstig wäre). Ansonsten werden auch in VB.NET alle Anweisungen brav nacheinander abgearbeitet und ein SELECT direkt nach einem INSERT wird ausgeführt bevor das nächste INSERT abgearbeitet wird. Okay ... wenn Du im Code erst alle INSERT's in Schleife abarbeitest und erst danach die SELECT's ausführen willst dann habe sich natürlich jede Menge INSERT's "dazwischen gedrängelt".

    Gruß

    Rainer
    @Textverarbeitung
    Danke für den Hinweis, ich wußte in der Tat nicht, dass MySql das auch kann :)

    @Spezialcode in einer separaten Tabelle
    es gibt ganz sicher für jeden Eintrag nur EINEN Spezialcode in dem Sachverhalt, den ich hier verfolge (das mit dem Rabattcode war nur so ein Beispiel aus dem täglichen Leben)
    deshalb habe ich auf die Auslagerung in eine separate Tabelle verzichtet.
    Das Prinzip der "Normalform" bei der Entwicklung von Datenbankmodellen und auch die Notwendigkeit der Vermeidung von redundanten Daten, ist mir durchaus geläufig.


    @dazwischendrängeln
    Aha, das ist interessant...
    nur das ich es richtig verstehe: eine Connection -> die Inserts und Selects werden nach der Reihe abgearbeitet und es gibt kein "dazwischendrängeln"

    eine zweite Connection kommt hinzu (weil z.B. eine zweite Instanz des Programms irgendwo anders läuft) -> die beiden kommen sich nicht ins Gehege, denn es werden erst die Inserts und Selects der einen Connection abgearbeitet und dann die der zweiten...

    also wenn das in der Konsquenz so ist, dann ist das ganz schön ausgefuchst :)

    lieben Gruß
    Gabi

    weiss_gabi schrieb:

    @Spezialcode in einer separaten Tabelle
    es gibt ganz sicher für jeden Eintrag nur EINEN Spezialcode in dem Sachverhalt, den ich hier verfolge (das mit dem Rabattcode war nur so ein Beispiel aus dem täglichen Leben)


    Are you sure? I don't believe this. ;)

    Oder kurz ... mag momentan so in der Praxis bei Dir sein, aber es können sich alle Angaben zu einem Kunden ändern (selbst Geburtstag ... Stichwort Fehleingabe), gibt es so gut wie keine Angaben die nur einmal vorkommen können (selbst Namen nicht, da die sich ändern können ... Stichwort Heirat) und die heutige Praxis hält in der Regel nie der Zukunft stand.

    Es gibt zu einem DS ganz sicher nur einen Wert der sich nie ändert und immer nur exakt einmal vorkommt ... der Primary Key.

    In dem Sinne ist es immer besser optimales Handling anzuwenden, anstatt irgendwann in der Zukunft ein echtes Problem zu bekommen. NIE NIE NIE auf etwas verzichten beim Datenmodell nur weil es heute noch nicht Praxis ist oder so heute noch nicht vorkommt ... in der Zukunft kommt nämlich all das vor wovon man heute glaubte das kommt nie vor. ;)

    weiss_gabi schrieb:

    @dazwischendrängeln
    Aha, das ist interessant...
    nur das ich es richtig verstehe: eine Connection -> die Inserts und Selects werden nach der Reihe abgearbeitet und es gibt kein "dazwischendrängeln"


    Richtig, eine Connection arbeitet wie eine Batch-Datei ... oder andersrum nachdem FiFo-Prinzip.

    weiss_gabi schrieb:

    eine zweite Connection kommt hinzu (weil z.B. eine zweite Instanz des Programms irgendwo anders läuft) -> die beiden kommen sich nicht ins Gehege, denn es werden erst die Inserts und Selects der einen Connection abgearbeitet und dann die der zweiten...


    Jein ... muss mich eh korrigieren, selbst parallele Connection in unterschiedlichen Threads kommen mit Last_Insert_ID sich nicht ins Gehege. Denn jede Connection arbeitet für sich ihre Statements nacheinander ab, völlig egal was die andere Connection tut. Daher ist auch Last_Insert nie (natürlich DBMS abhängig) Datenbank bezogen sondern auf die Client-Connection bezogen die den Insert ausgelöst hat.

    Aber gleichzeitiger Zugriff auf ein und diesselbe DB ist durchaus möglich und hier können sich Connections auch in die Quere kommen weil die Connections eben parallel bearbeitet werden und nicht jede Connection nacheinander. Ändert also Connection1 einen DS und Connection2 will den nun auch ändern gibt es für Connection2 eine Lock-Meldung das der DS nicht bearbeitet werden kann weil er bereits von einem anderen User bearbeitet wird.

    Gruß

    Rainer
    Ich noch einmal :)

    also mittlerweile konnte ich den Fehler reproduzieren und muß zu meiner Schande gestehen, dass ich an der ganz falschen Stelle nach einer Lösung gesucht hatte....

    der Fehler lag gar nicht bei der Abfrage der Datensatznummer (LAST_INSERT_ID) sondern bei den Zeichen, die ich um die Datensatznummer herum gestrickt hatte.
    Dort hatte ich Zufallszeichen nach dem folgenden Prinzip gebildet:

    VB.NET-Quellcode

    1. startpos = CInt(Int(Rnd() * 100 ) Mod 50)
    2. gemisch = gemisch & Mid$(ausgang, startpos, 1)


    wobei die Stringvariable ausgang 50 Zeichen enthielt.
    Dabei hatte ich über sehen, dass die Integer startpos auch 0 sein kann, nämlich genau dann, wenn die CInt(Rnd()) gleich 0 oder 0,5 oder 1 ist.
    Das ist aber bei der Funktion MID nicht erlaubt :(

    Da das auch nicht so oft vorkam (ist ja klar), trat der Fehler auch nur sporadisch auf.

    Wieder was gelernt: das nächste mal schaue ich mir die Fehlermeldung ganz genau an.
    Und vermeide Methoden, die aus dem veralteten VB6 mit rübergeschleppt wurden.
    Statt Mid$ hat man in .Net String.SubString().
    Der MB6-Schrott ist 1-Basiert, also der erste Index in VB6 ist immer 1, während in .Net ein Index immer mit 0 beginnt.
    Erstens ist 1-basiertheit konzeptioneller Schrott (also mathematisch unlogisch, denn die erste Zahl ist 0, nicht 1 (wie alt ist ein Baby bei seiner Geburt? 0 oder 1 Jahr?). Zweitens gibts immer diese tückischen Probleme, wenn man 1-basiertes Zeugs in der 0-basierten Umgebung von .Net verwendet.
    Also Mid$, Left$, Right$, Collection - so 1-basierter VB6-Schrott, der verboten gehört. Und gibt glaub noch mehr, weißichgrad nicht auswendig.
    Guck dir die String-Klasse im ObjectBrowser an - da kann einem teilw. ein Licht aufgehen.
    Danke für den Tipp :)

    in der Tat bin ich es auch aus anderen Sprachen gewohnt, dass das erste Zeichen eines String mit 0 gezählt wird, deshalb habe ich mir auch nichts dabei gedacht, das meine startpos-Variable auch mal 0 sein darf....

    ich habe jetzt:

    VB.NET-Quellcode

    1. For i = 1 To laenge
    2. startpos = CInt(Int(Rnd() * 100) Mod 50)
    3. gemisch = gemisch & ausgang.Substring(startpos, 1)
    4. Next


    geschrieben, wobei laenge die Länge des zu erstellenden Codes ist, also wieviele Zeichen im gemisch drin sind, und ausgang sind 50 unterschiedliche Buchstaben (Groß- und Kleinschreibung).

    lieben Gruß
    Gabi