none
Über Datarecieved Ereignis MySQL Befehle auslösen RRS feed

  • Frage

  • verwendetes Datenbanksystem: MySQL

    Hallo,
    Ich habe ein Datarecevied Ereignis mit dem ich einen Barcode einlese und dadurch dann MySQL Kommandos ausführen will.
    private void Form1_Load(object sender, EventArgs e)
            {
                dateTimePicker1.Format = DateTimePickerFormat.Time;
                string Zeit = dateTimePicker1.Text;
               
                SerialPort mySerialPort = new SerialPort("COM1");
                mySerialPort.BaudRate = 9600;
                mySerialPort.Parity = Parity.None;
                mySerialPort.StopBits = StopBits.One;
                mySerialPort.DataBits = 8;
                mySerialPort.Handshake = Handshake.None;
    
                mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceviedHandler);
                mySerialPort.Open();
                                       
            }
    
            private void DataReceviedHandler(object sender, SerialDataReceivedEventArgs e)
            {
    
                SerialPort sp = (SerialPort)sender;
                string indata = sp.ReadExisting();
                Code = indata;
                int Nummer = Convert.ToInt32(Code);
                string mysql = "UPDATE Besucher set Ende = ? WHERE LfdNr = ?";
                string mysql2 = "INSERT INTO Archiv (Name, Firma, Begleitperson, Beginn, Ende, Kennz, Datum, Bez) SELECT Name, Firma, Begleitperson, Beginn, Ende, Kennz, Datum, Bez FROM Besucher where Ende > 0";
                string mysql3 = "DELETE FROM Besucher where Ende > 0";
                OdbcCommand DbCommand = new OdbcCommand();
                OdbcConnection DbConnection = new OdbcConnection(con);
                try
                {
                    DbConnection.Open();
                    DbCommand.Parameters.Add("@ende", OdbcType.Char).Value = dateTimePicker1.Text;
                    DbCommand.Parameters.Add("@LfdNr", OdbcType.Char).Value = Nummer;
                    DbCommand.Connection = DbConnection;
                    DbCommand.CommandText = mysql;      //Ende einfügen
                    DbCommand.ExecuteNonQuery();
                    DbCommand.CommandText = mysql2;     // in Archiv schreiben
                    DbCommand.ExecuteNonQuery();
                    DbCommand.CommandText = mysql3;     // Aus aktueller Tabelle löschen
                    DbCommand.ExecuteNonQuery();
    
                }
    
                catch (Exception f)
                {
                    MessageBox.Show(f.Message);
                }
    
                finally
                {
                    DbConnection.Dispose();
                }
    }
    


    Der Code den ich habe läuft fehlerfrei durch und es werden auch keine Compilerfehler oder Warnungen angezeigt und man kann es auch durchdebuggen und es steht das richtige in den Variblen. Der connection string ist auch richtig.
    Aber die MySQL Befehle werden einfach nicht ausgeführt da muss irgendetwas falsch sein.

    Könnt ihr mir helfen?

    mfg

     

    Montag, 10. Oktober 2011 07:00

Antworten

  • Hallo,

    es ist grundsätzlich keine gute Idee im DataReceived Ereignis umfangreichere Aufgaben durchzuführen.
    Da  Du hier (potentiell) umfangreichere Änderungen vornimmst, blockierst Du wiederum den SerialPort.
    Und schon gar nicht sollte man auf Steuerelemente zurückgreifen, denn das Ereignis tritt auf einem Hintergrund-Thread auf.

    Ein mögliches Vorgehen wäre hier das Abstellen der Daten in einer Queue,
    die von einem weiteren Thread abgearbeitet wird und die Änderungen in der Datenbank vornimmt.

    Mit .NET 4.0 und der TPL bietet sich dafür die BlockingCollection an,
    siehe dazu: Übersicht über BlockingCollection

    Im übrigen solltest Du bei MySQL besser den .NET Connector als managed Provider einsetzen.
    Und anstatt drei Einzelbefehle könnte sich eine Prozedur lohnen,
    die die Zahl der Datenbank-Roundtrips auf einen reduziert.

    Gruß Elmar

    Montag, 10. Oktober 2011 09:04
    Beantworter
  • Hallo,

    da Du von einer Queue[1] bisher nicht gehört hast, so ist wahrscheinlich sinnvoller zunächst mit kleineren Brötchen anzufangen. ;-)

    Wie auch im DataReceived Ereignis beschrieben, auf ein Steuerelement darfst Du dort nicht direkt zugreifen,
    da die Daten über einen Hintergrund-Thread geliefert werden.
    Ein Zugriff auf Steuerelemente muss über den Vordergrund-Thread erfolgen,
    was man über Control.Invoke erreicht.

    Wobei es in Deinem Falle ohnhin günstiger wäre, zusätzliche Daten über interne Eigenschaften
    anstatt direkt über die Steuerelemente abzufragen.
    Denn derzeit ist es mehr oder weniger zufällig, was z. B gerade im dateTimePicker1.Text drinne steht.
    Ändert der Anwender gerade, so kann das auch nur ein "halbes" / "halbrichtiges" Datum sein.

    Deswegen solltest Du auf eine korrekte Eingabe prüfen und diese in ein internes Feld speichern.

    Zum Datenbankzugriff: Und wenn es sich dabei wie dem nach zu urteilen um ein Datum handelt,
    solltest Du mit DateTime arbeiten und dies ebenfalls auf Datenbankebene tun, siehe
    http://dev.mysql.com/doc/refman/5.1/en/datetime.html

    Das INSERT / DELETE Paar wiederum sollte auf die LfdNr beschränkt werden.
    Sonst gibt sind bei parallelen Zugriffen Probleme vorprogrammiert (abseits davon das es ineffizienter ist).
    Ergänze das Where Kriterium jeweils um die LfdNr:

    "... WHERE LfdNr = ? AND Ende > 0";
    
    

    Wenn Du das umsetzen konntest, so können wir einen Schritt weitergehen.

    Gruß Elmar

    [1]Zur Queue:

    Eine Queue gehört zum Standard-Repertoire, das ein Programmierer beherrschen sollte / muß,
    siehe dazu u. a.: Generische Auflistungen in .NET Framework.
    Die angesprochene BlockingCollection und Co. sind Erweiterungen die für multi-threaded Zugriffe optimiert sind.
    Aber bevor man sich damit auseinandersetzt, sollte man erst die Grundlagen (wie oben) verstanden haben.

    Queues kommen aber auch anderweitig z. B. bei Hardware vor:
    So arbeitet bereits ein SerialPort über eine (FIFO)-Queue wie dem UART-16550 Klassiker
    (auch wenn das heute meist zugunsten von USB aufgeben wurde)

    Montag, 10. Oktober 2011 18:47
    Beantworter
  • Danke euch beiden, ich hab es hingebracht  Mit Variblen, einer extra Form und etwas mehr code, aber es funtktioniert.

     

    private void Zeitprüfung_Load(object sender, EventArgs e)
            {
                dateTimePicker1.Format = DateTimePickerFormat.Time;
                string time = dateTimePicker1.Text;
                SerialPort mySerialPort = new SerialPort("COM1");
                mySerialPort.BaudRate = 9600;
                mySerialPort.Parity = Parity.None;
                mySerialPort.StopBits = StopBits.One;
                mySerialPort.DataBits = 8;
                mySerialPort.Handshake = Handshake.None;
                mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceviedHandler);
                mySerialPort.Open();
            }
    
            private void DataReceviedHandler(object sender, SerialDataReceivedEventArgs e)
            {
                SerialPort sp = (SerialPort)sender;
                indata = sp.ReadExisting();
                Nummer = Convert.ToInt32(indata);
                indata = dateTimePicker1.Text;
                
                //string test = "08:12:14";
                //TimeSpan.Parse(time);
                string mysql = "UPDATE Besucher set Ende = ? where LfdNr = ?";
                string mysql2 = "INSERT INTO Archiv (Name, Firma, Begleitperson, Beginn, Ende, Kennz, Datum, Bez,LfdNr,Belehrung) SELECT Name, Firma, Begleitperson, Beginn, Ende, Kennz, Datum, Bez,LfdNr,Belehrung FROM Besucher where Ende > 0";
                string mysql3 = "DELETE FROM Besucher where LfdNr= ?";
                OdbcCommand DbCommand = new OdbcCommand();
                OdbcConnection DbConnection = new OdbcConnection(con);
                try
                {
                    DbConnection.Open();
                    DbCommand.Parameters.AddWithValue("@Zeit", OdbcType.Time).Value = dateTimePicker1.Text;
                    DbCommand.Parameters.AddWithValue("@LfdNr", OdbcType.VarChar).Value = Nummer;
                    DbCommand.Parameters.AddWithValue("@LfdNr2", OdbcType.VarChar).Value = Nummer;
                    DbCommand.Connection = DbConnection;
                    DbCommand.CommandText = mysql;      //Ende einfügen
                    DbCommand.ExecuteNonQuery();                    //Die Eingabezeichenfolge hat das falsche format
                    DbCommand.CommandText = mysql2;     // in Archiv schreiben
                    DbCommand.ExecuteNonQuery();                  //Es können scheinbar nur 2 Befehle pro Connection ausgeführt werden?
                    DbCommand.CommandText = mysql3;
                    DbCommand.ExecuteNonQuery();
                                              }
    
                catch (Exception f)
                {
                    MessageBox.Show(f.Message);
                }
    
                finally
                {
                    DbConnection.Dispose();
                    MessageBox.Show("Vielen Dank, Der Barcode wurde erfolgreich gescannt.");
                    sp.Close();
                }
    
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                Close();
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
                string mysql3 = "DELETE FROM Besucher where LfdNr= ?";
                OdbcCommand DbCommand = new OdbcCommand();
                OdbcConnection DbConnection = new OdbcConnection(con);
                try
                {
                    DbConnection.Open();
                    DbCommand.Parameters.AddWithValue("@LfdNr", OdbcType.VarChar).Value = Nummer;
                    DbCommand.Connection = DbConnection;
                    DbCommand.CommandText = mysql3;
                    DbCommand.ExecuteNonQuery();
                
                }
    
                catch (Exception f)
                {
                    MessageBox.Show(f.Message);
                }
    
                finally
                {
                    DbConnection.Dispose();
                    // MessageBox.Show("Vielen Dank, Der Barcode wurde erfolgreich gescannt.");
                }
                Close();
            }
    

     

    Mittwoch, 12. Oktober 2011 12:28

Alle Antworten

  • hmm, sicher das du nicht ein Logikproblem hast?

    Dein zuerst ausgeführtes UPDATE setzt einen Wert für die Spalte Ende. Danach löscht du alle Datensätze welche einen Wert in der Spalte Ende haben... Auch ist die Frage, ob MySQL die VARCHAR()-Spalte Ende implizit in ein Integer konvertieren kann oder umgekehrt, so das deine WHERE-Bedingung im INSERT Sinn ergibt.

    Zum Testen solltest du deine drei SQL-Anweisungen mal direkt gegen MySQL testen.

    Montag, 10. Oktober 2011 07:13
  • Hallo,

    es ist grundsätzlich keine gute Idee im DataReceived Ereignis umfangreichere Aufgaben durchzuführen.
    Da  Du hier (potentiell) umfangreichere Änderungen vornimmst, blockierst Du wiederum den SerialPort.
    Und schon gar nicht sollte man auf Steuerelemente zurückgreifen, denn das Ereignis tritt auf einem Hintergrund-Thread auf.

    Ein mögliches Vorgehen wäre hier das Abstellen der Daten in einer Queue,
    die von einem weiteren Thread abgearbeitet wird und die Änderungen in der Datenbank vornimmt.

    Mit .NET 4.0 und der TPL bietet sich dafür die BlockingCollection an,
    siehe dazu: Übersicht über BlockingCollection

    Im übrigen solltest Du bei MySQL besser den .NET Connector als managed Provider einsetzen.
    Und anstatt drei Einzelbefehle könnte sich eine Prozedur lohnen,
    die die Zahl der Datenbank-Roundtrips auf einen reduziert.

    Gruß Elmar

    Montag, 10. Oktober 2011 09:04
    Beantworter
  • Danke erstmal euch beiden,

    also die drei einzelbefehle funktionieren hab ich ausprobiert, und der sinn von dem löschen ist dass wenn man rausgeht dann wird das Ende des besuches bspw eingeschrieben, und der besucher aus der "Besucher im Betrieb" Tabelle gelöscht und in ein Archiv geschrieben.

    @hört sich logisch an das mit den Threads verstehe ich noch nicht richtig wie das alles ablaufen muss/soll ich wollt jetzt mal einer textbox den gelesenen wert aus datarecieved zuweisen da kommt dann so ein Threadfehler:

    Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement textBoxBarcode erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.

    Abstellen der Daten in einer "Queue", nich lachen das wort höre ich heute zum ersten mal.

    ich werde mir das mal alles anschaun.

    danke derweil

    mfg

    Montag, 10. Oktober 2011 09:21
  • Hallo,

    da Du von einer Queue[1] bisher nicht gehört hast, so ist wahrscheinlich sinnvoller zunächst mit kleineren Brötchen anzufangen. ;-)

    Wie auch im DataReceived Ereignis beschrieben, auf ein Steuerelement darfst Du dort nicht direkt zugreifen,
    da die Daten über einen Hintergrund-Thread geliefert werden.
    Ein Zugriff auf Steuerelemente muss über den Vordergrund-Thread erfolgen,
    was man über Control.Invoke erreicht.

    Wobei es in Deinem Falle ohnhin günstiger wäre, zusätzliche Daten über interne Eigenschaften
    anstatt direkt über die Steuerelemente abzufragen.
    Denn derzeit ist es mehr oder weniger zufällig, was z. B gerade im dateTimePicker1.Text drinne steht.
    Ändert der Anwender gerade, so kann das auch nur ein "halbes" / "halbrichtiges" Datum sein.

    Deswegen solltest Du auf eine korrekte Eingabe prüfen und diese in ein internes Feld speichern.

    Zum Datenbankzugriff: Und wenn es sich dabei wie dem nach zu urteilen um ein Datum handelt,
    solltest Du mit DateTime arbeiten und dies ebenfalls auf Datenbankebene tun, siehe
    http://dev.mysql.com/doc/refman/5.1/en/datetime.html

    Das INSERT / DELETE Paar wiederum sollte auf die LfdNr beschränkt werden.
    Sonst gibt sind bei parallelen Zugriffen Probleme vorprogrammiert (abseits davon das es ineffizienter ist).
    Ergänze das Where Kriterium jeweils um die LfdNr:

    "... WHERE LfdNr = ? AND Ende > 0";
    
    

    Wenn Du das umsetzen konntest, so können wir einen Schritt weitergehen.

    Gruß Elmar

    [1]Zur Queue:

    Eine Queue gehört zum Standard-Repertoire, das ein Programmierer beherrschen sollte / muß,
    siehe dazu u. a.: Generische Auflistungen in .NET Framework.
    Die angesprochene BlockingCollection und Co. sind Erweiterungen die für multi-threaded Zugriffe optimiert sind.
    Aber bevor man sich damit auseinandersetzt, sollte man erst die Grundlagen (wie oben) verstanden haben.

    Queues kommen aber auch anderweitig z. B. bei Hardware vor:
    So arbeitet bereits ein SerialPort über eine (FIFO)-Queue wie dem UART-16550 Klassiker
    (auch wenn das heute meist zugunsten von USB aufgeben wurde)

    Montag, 10. Oktober 2011 18:47
    Beantworter
  • btw, das Ereignis DataRecieved wird unter Umständen auch dann schon ausgelöst, wenn noch nicht alle deine erwartete Daten vorhanden sind. D.h. du arbeitest gegebenenfalls nur mit einem Teilstring.
    Dienstag, 11. Oktober 2011 06:51
  • Danke euch beiden, ich hab es hingebracht  Mit Variblen, einer extra Form und etwas mehr code, aber es funtktioniert.

     

    private void Zeitprüfung_Load(object sender, EventArgs e)
            {
                dateTimePicker1.Format = DateTimePickerFormat.Time;
                string time = dateTimePicker1.Text;
                SerialPort mySerialPort = new SerialPort("COM1");
                mySerialPort.BaudRate = 9600;
                mySerialPort.Parity = Parity.None;
                mySerialPort.StopBits = StopBits.One;
                mySerialPort.DataBits = 8;
                mySerialPort.Handshake = Handshake.None;
                mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceviedHandler);
                mySerialPort.Open();
            }
    
            private void DataReceviedHandler(object sender, SerialDataReceivedEventArgs e)
            {
                SerialPort sp = (SerialPort)sender;
                indata = sp.ReadExisting();
                Nummer = Convert.ToInt32(indata);
                indata = dateTimePicker1.Text;
                
                //string test = "08:12:14";
                //TimeSpan.Parse(time);
                string mysql = "UPDATE Besucher set Ende = ? where LfdNr = ?";
                string mysql2 = "INSERT INTO Archiv (Name, Firma, Begleitperson, Beginn, Ende, Kennz, Datum, Bez,LfdNr,Belehrung) SELECT Name, Firma, Begleitperson, Beginn, Ende, Kennz, Datum, Bez,LfdNr,Belehrung FROM Besucher where Ende > 0";
                string mysql3 = "DELETE FROM Besucher where LfdNr= ?";
                OdbcCommand DbCommand = new OdbcCommand();
                OdbcConnection DbConnection = new OdbcConnection(con);
                try
                {
                    DbConnection.Open();
                    DbCommand.Parameters.AddWithValue("@Zeit", OdbcType.Time).Value = dateTimePicker1.Text;
                    DbCommand.Parameters.AddWithValue("@LfdNr", OdbcType.VarChar).Value = Nummer;
                    DbCommand.Parameters.AddWithValue("@LfdNr2", OdbcType.VarChar).Value = Nummer;
                    DbCommand.Connection = DbConnection;
                    DbCommand.CommandText = mysql;      //Ende einfügen
                    DbCommand.ExecuteNonQuery();                    //Die Eingabezeichenfolge hat das falsche format
                    DbCommand.CommandText = mysql2;     // in Archiv schreiben
                    DbCommand.ExecuteNonQuery();                  //Es können scheinbar nur 2 Befehle pro Connection ausgeführt werden?
                    DbCommand.CommandText = mysql3;
                    DbCommand.ExecuteNonQuery();
                                              }
    
                catch (Exception f)
                {
                    MessageBox.Show(f.Message);
                }
    
                finally
                {
                    DbConnection.Dispose();
                    MessageBox.Show("Vielen Dank, Der Barcode wurde erfolgreich gescannt.");
                    sp.Close();
                }
    
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                Close();
            }
    
            private void button2_Click(object sender, EventArgs e)
            {
                string mysql3 = "DELETE FROM Besucher where LfdNr= ?";
                OdbcCommand DbCommand = new OdbcCommand();
                OdbcConnection DbConnection = new OdbcConnection(con);
                try
                {
                    DbConnection.Open();
                    DbCommand.Parameters.AddWithValue("@LfdNr", OdbcType.VarChar).Value = Nummer;
                    DbCommand.Connection = DbConnection;
                    DbCommand.CommandText = mysql3;
                    DbCommand.ExecuteNonQuery();
                
                }
    
                catch (Exception f)
                {
                    MessageBox.Show(f.Message);
                }
    
                finally
                {
                    DbConnection.Dispose();
                    // MessageBox.Show("Vielen Dank, Der Barcode wurde erfolgreich gescannt.");
                }
                Close();
            }
    

     

    Mittwoch, 12. Oktober 2011 12:28
  • Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement textBoxBarcode erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.

    Hallo Schormi,

    das passiert jedem, der mit Serialport anfängt. Bitte schau Dir diesen code an: (leider in VB):

    9. Wie führt man Serial-COM Portkommunikation ein ?

    Ich würde Deine Aufgabe erst einmal programmtechnisch  entkoppeln. Versuche mit dem Code die Scanner Daten in eine Textbox zu lesen. Dann solltest Du den SQL Zugriff machen.  Auf keinen Fall im Datareceived Event, wie Elmar schon gesagt hat.

    schöne Grüsse Ellen

     


    Ich benutze/ I'm using VB2008 & VB2010
    Mittwoch, 12. Oktober 2011 15:25
  • Also ich hab jetzt die Daten von Datarecieved in eine Varible schaut dann so aus:

     

    private void DataReceviedHandler(object sender, SerialDataReceivedEventArgs e)
            {
                    SerialPort sp = (SerialPort)sender;
                    Nummer = sp.ReadExisting();
    
           }
    
    private void button2_Click(object sender, EventArgs e)
            {
                try
                {
                    string mysql3 = "DELETE FROM Besucher where LfdNr= ?";
                    OdbcCommand DbCommand = new OdbcCommand();
                    OdbcConnection DbConnection = new OdbcConnection(con);
                    try
                    {
                        DbConnection.Open();
                        DbCommand.Parameters.AddWithValue("@LfdNr", OdbcType.VarChar).Value = Nummer;
                        DbCommand.Connection = DbConnection;
                        DbCommand.CommandText = mysql3;
                        DbCommand.ExecuteNonQuery();
    
                    }
    
                    catch (Exception f)
                    {
                        MessageBox.Show(f.Message);
                    }
    
                    finally
                    {
                        DbConnection.Dispose();
                        // MessageBox.Show("Vielen Dank, Der Barcode wurde erfolgreich gescannt.");
                    }
                    Close();
                }
                catch (ObjectDisposedException f)
                {
                    MessageBox.Show(f.Message);
                    MessageBox.Show("Es ist ein Fehler aufgetreten auf Fertigbutton, Bitte ein paar Sekunden warten und weitermachen.");
                }
            }



     Dieser Code ist auf einer extra Form, wenn ich nun die Form aufrufe und sie ohne einen Barcode zu lesen wieder schließe taucht ObjectDisposedException auf. Heißt ja, das man z.B. auf einen Stream zugreift der schon geschlossen ist versteh das aber nicht ganz, außerdem kan ich den fehler nicht mit Catch abfangen, wie kann man den Fehler ausbügeln? mfg

    • Bearbeitet Schormi Mittwoch, 19. Oktober 2011 06:04
    Mittwoch, 19. Oktober 2011 06:00
  • Hallo Schormi,

    Bitte, immer für eine Neue Frage einen Neuen Thread aufmachen.

    Grundsätzlich ist zu jedem neuen Thema ein eigener Thread zu öffnen, denn dadurch wird die Übersicht gewahrt.

    Ein eigener Thread nutzt der Kommunikation und trägt der Übersichtlichkeit in einem Forum bei.

    Danke und Grüße,

    Robert

    Montag, 31. Oktober 2011 13:51
    Moderator