none
Datenbank RRS feed

  • Frage

  • Hallo

     

    Ich habe eine VB.NET-Anwendung, die auf zwei Mysql-Datenbanken zugreift. Zugriff, Auslesen und Eintragen in die Datenbank klappt.

    Die Methoden zum Auslesen und eintragen in die Datenbank, werden von einem Timer, alle 1s aufgerufen. Die Anwendung soll als Hintergrund - Prozess laufen. Die Anwendung dient dazu daten von einem Microcontroller über SerialPort auszulesen und sie dann selbständig (m.H. des Timers) in die Datenbank eintragen. Soweit so gut!

    Problem ist jetzt nur, das es immer wieder Fehlermeldung gibt, wie z.B. Datenbank must be solid and open, oder Objektverweis nicht korrekt usw...

    Das Eintragen und so funktioniert aber weiterhin.... nur die Fehlermeldungen nerven!

    Bin deswegen hingegangen und habe den Timer von 1s auf 7s hochgesetzt und siehe da die Fehlermeldung waren nach 4 Std eintragen nur auf eine geschrumpft...

    Meine Frage ist jetzt:

    Wie schnell kann ein Datenbankzugriff sein? Es muss ja ein Datenbankobjekt ins Leben rufen, die Verbindung jeweils 2mal aufgebaut und wieder beendet werden usw...! Wie schnell geht so etwas? Oder ist generell meine Vorgehensweise mit einem Timer falsch? In meinen Fall kann nicht einer den ganzen Tag am PC sitzen und immer einen Button drücken um den Messwerte einzutragen...

     

    Gruß

     

     

     

     

     

    Mittwoch, 20. April 2011 10:32

Antworten

  • Ohne die Details zu kennen, würde ich erst einmal asynchrone Ereignisse
    verantwortlich machen, die dazu führen, dass die Datenbank gleichzeitig von
    2 parallelen (asynchronen) threads angesprochen wird. Versuche mal die zu
    speichernden Daten in eine Queue einzureihen und die Elemente der Queue in
    nur einem threads sequentiell abzuarbeiten.
     
    --
    Viele Grüße
    Peter
     
     
    Mittwoch, 20. April 2011 17:42
  • Die Idee ist ganz einfach.
     
    Es gibt eine Queue in der Klasse. Die Ereignisroutine, die die Daten empfängt, ist so aufgebaut, dass sie außer der Variablen für die Queue keine weiteren außerhalb der Routine deklarierten Variablen nutzt. Damit wird die Routine parallel verwendbar. Wenn die Ereignisse sehr kurz hintereinander kommen, dann wird die Routine mehrmals parallel abgearbeitet. Die Routine baut aus jedem Ereignis ein Objekte, welches in die Queue eingereiht. Zusätzlich wird gleichzeitig ein Ereignis an die Verarbeitungsroutine gesendet.
     
    Die Verarbeitungsroutine wartet auf das Ereignis. Wenn das eintrifft, dann wird aus der Queue ein Objekt entnommen und verarbeitet. Am Ende der Verarbeitung eines Objekt wird das nächste Objekt aus der Queue entnommen und verarbeitet. Wenn die Queue leer ist, wird wieder auf das Ereignis gewartet.
     
    --
    Viele Grüße
    Peter
    Donnerstag, 21. April 2011 19:25
  • Hallo,

    Du hast generell ein Problem mit threadübergreifenden Zugriffen.
    Das SerialPort.DataReceived Ereignis wird auf einem sekundären (ThreadPool) Thread aufgerufen.
    Das dürfte Dir auch aufgefallen sein, denn damit sind auch direkte Zugriffe Steuerelemente tabu,
    siehe Gewusst wie: Threadsicheres Aufrufen von Windows Forms-Steuerelementen

    Deine Lösung CheckForIllegalCrossThreadCalls einfach abzuwürgen rächt sich dann nur an anderer Stelle:

    Denn Deine Funktionen wie getZustandBox1 können so parallel von zwei Threads aufgerufen werden,
    einmal vom Windows Timer (der läuft auf dem UI-Thread) und noch mal via DataReceived auf einem
    Hintergrundthread.

    Neben den Ausnahmen kannst Du musst mit fehlerhaften Rückgaben/Setzen rechnen,
    denn Deine Zustand(n) Variablen können so auch mal "lügen".
    Und Datenbank-Klassen (MySqlConnection und Co.) sollte man niemals über mehrere Threads hinweg verwenden.

    Eine simple Lösung ohne größeren Umbau gibt es, soweit ich das bisher sehe nicht.
    Denn würdest Du die Zugriffe via SyncLock kapseln, was minimal notwendig wäre,
    wird Dir durch unverhersagbare Überschneidungen von Timer/DataReceived Thread
    über kurz oder lang eine Blockierung oder Race Condition um die Ohren fliegen.

    Peter Fleischers Vorschlag ist insofern der richtige Ansatz, ohne einen größeren Umbau wird
    es dabei aber nicht abgehen. (Und zuvor solltest Du Hannes Vorschlag folgen und Deinen
    Code für Option Strict On tauglich machen).

    Ein Beispiel wie eine solche Queue aussehen kann, hatte ich mal in den VB-Newsgroups gegeben:
    http://www.ms-news.net/f827/event-aus-einer-klasse-soll-auf-form-etwas-ausfuehren-9268354.html
    (das dortige Terminal wäre Deinem SerialPort sehr ähnlich).

    Gruß Elmar

    Freitag, 22. April 2011 10:51
    Beantworter
  • Noch ein kleines Beispiel, wie Du Deinen Code besser kapselst und lesbarer machst:

    'Visual Basic 2008 - .net 3.5 - Any CPU
     'Zustand je nach Datenbank auslesen und zurückgeben
     Private Function getZustand(ByVal Databasename As String) As Integer
     Dim ReturnValue As Integer
     Try
      Using myConnection As New MySql.Data.MySqlClient.MySqlConnection(String.Concat(TheConnectionString, Databasename, ";"))
      myConnection.Open()
      Using mycommand As New MySql.Data.MySqlClient.MySqlCommand("SELECT zustand FROM boxzustand WHERE id=1;", myConnection)
       Try
       ReturnValue = Convert.ToInt32(mycommand.ExecuteScalar())
       Catch ex As Exception
       Throw ex
       Finally
       myConnection.Close()
       End Try
      End Using
      End Using
     Catch ex As Exception
      Throw ex
     End Try
     Return ReturnValue
     End Function
     'Box je nach Datenbank und Zustand updaten ( also ein oder ausschalten )
     Private Sub UpdateBox(ByVal Databasename As String, ByVal Value As Integer)
     Try
      Using myConnection As New MySql.Data.MySqlClient.MySqlConnection(String.Concat(TheConnectionString, Databasename, ";"))
      myConnection.Open()
      Using mycommand As New MySql.Data.MySqlClient.MySqlCommand("UPDATE boxzustand SET zustand=@zustand WHERE id=1;", myConnection)
       mycommand.Parameters.AddWithValue("@zustand", Value)
       Try
       mycommand.ExecuteNonQuery()
       Catch ex As Exception
       Throw ex
       Finally
       myConnection.Close()
       End Try
      End Using
      End Using
     Catch ex As Exception
      Throw ex
     End Try
     End Sub
    

     



    Noch ein paar erklärende Links:

    Using keyword, Finally, String.Concat()


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/

    Dienstag, 26. April 2011 06:11

Alle Antworten

  • Hallo _Tim,

    am Besten wäre es wenn Du Deinen Code zum Auslesen und Eintragen inklusive Connect zur Datenbank einmal zeigst. Ebenso nützlich wäre es zu wissen, welche MySQL Version Du benutzt, sowohl als DB als auch als connector.

    Gruß


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Mittwoch, 20. April 2011 11:02
  • hier der code:

     

    Imports System.IO.Ports.SerialPort
    Imports MySql.Data.MySqlClient
    Public Class Form1
        'Attribute
        Private MyConnectionBox1, MyConnectionBox2 As MySqlConnection
        Private MyCommandBox1, MyCommandBox2 As MySqlCommand
       

        Private eintrag As String
        Private WertListBox1 As String
        Private WertListBox2 As String
        Private SchaltenBox1 As String
        Private SchaltenBox2 As String

        'SerialPort öffnen
        Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            Try
                SerialPort1.PortName = "COM6"
                SerialPort1.Open()
            Catch ex As Exception
                lbl_FehlermeldungenAllgemein.Text = "1.Fehler: " & ex.Message
            End Try

            Timer1.Enabled = True
        End Sub
        'SerialPort Werte einlesen
        Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
            Dim in_string As String
            Dim in_string2 As String
            Dim in_string3 As String


            Try
               
                Control.CheckForIllegalCrossThreadCalls = False

                in_string = SerialPort1.ReadLine


                If (in_string.IndexOf("1", 0, 1) <> -1) Then
                    'ID "1" wird abgeschnitten
                    in_string2 = in_string.Remove(0, 1)
                    '.Replace(alter Wert der ersetzt wird,neuer Wert der eingetragen wird)
                    in_string3 = in_string2.Replace(Chr(13), Nothing)
                    WertListBox1 = in_string3
                    If (Me.getZustandBox1 = 1) Then
                        lst_AusgabeWerteBox1.Items.Insert(0, in_string3)
                    End If
                End If

                If (in_string.IndexOf("2", 0, 1) <> -1) Then

                    in_string2 = in_string.Remove(0, 1)
                    '.Replace(alter Wert der ersetzt wird,neuer Wert der eingetragen wird)
                    in_string3 = in_string2.Replace(Chr(13), Nothing)
                    WertListBox2 = in_string3
                    If (Me.getZustandBox2 = 1) Then
                        lst_AusgabeWerteBox2.Items.Insert(0, in_string3)
                    End If
                End If


            Catch ex As Exception
                lbl_FehlermeldungenAllgemein.Text = "2.Fehler: " & ex.Message
            End Try

        End Sub
        'Timer
        Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick

            If Me.getZustandBox1 = 1 Then
                'Box1 eingeschaltet
                Me.WerteInDBEintragenBox1()
                lbl_StatusBox1.Text = "Status: EINGESCHALTET"
            Else
                'Box1 ausgeschaltet
                Me.lst_AusgabeWerteBox1.Items.Clear()
                lbl_FehlermeldungBox1.Text = ""
                lbl_FehlermeldungenAllgemein.Text = ""
                lbl_StatusBox1.Text = "Status: AUSGESCHALTET"
            End If


            If Me.getZustandBox2 = 1 Then
                'Box2 eingeschaltet
                Me.WerteInDBEintragenBox2()
                lbl_StatusBox2.Text = "Status: EINGESCHALTET"
            Else
                'Box2 ausgeschaltet
                Me.lst_AusgabeWerteBox2.Items.Clear()
                lbl_FehlermeldungBox2.Text = ""
                lbl_FehlermeldungenAllgemein.Text = ""
                lbl_StatusBox2.Text = "Status: AUSGESCHALTET"
            End If


            Me.ZustandBox1Box2() 'Überwachung falls über Homepage geschaltet wird
        End Sub
        'Werte von BOX 1 in DB eintragen
        Private Sub WerteInDBEintragenBox1()
            Dim KW As Integer
            Dim EffektivWert As Double
            Dim Spannung As Double
            Dim Leistung As Double

            'DatePart gibt die KW-Woche zurück
            KW = DatePart(DateInterval.WeekOfYear, Date.Today(), FirstDayOfWeek.Monday, FirstWeekOfYear.FirstFourDays)

            Try
                Me.MitDBVerbindenBox1()

                If WertListBox1 > 0 Then
                    'Wert in Datenbank schreiben
                    EffektivWert = Convert.ToDouble(WertListBox1.Replace(".", ","))

                    Spannung = "230"
                    Leistung = EffektivWert * Spannung

                    'EffektivWert.ToString.Replace(",", ".")weil Mysql akzeptiert nur Punkte(z.B.12.345) und VB nur Kommas(z.B. 12,345)
                    MyCommandBox1 = New MySqlCommand("INSERT INTO boxwerte (strom,spannung,leistung,tag,monat,jahr,KW) VALUES ('" & _
                                                 EffektivWert.ToString.Replace(",", ".") & "','" & Spannung & "','" & Leistung.ToString.Replace(",", ".") & "','" & _
                                                 Date.Now.Day & "','" & Date.Now.Month & "','" & Date.Now.Year & "','" & KW & "');", MyConnectionBox1)

                    MyCommandBox1.ExecuteNonQuery()
                End If
                Me.DBVerbindungTrennenBox1()
            Catch ex As Exception
                lbl_FehlermeldungBox1.Text = "3. Fehler: " & ex.Message
            End Try

        End Sub
        'Werte von BOX 2 in DB eintragen
        Private Sub WerteInDBEintragenBox2()
            Dim KW As Integer
            Dim EffektivWert As Double
            Dim Spannung As Double
            Dim Leistung As Double


            'DatePart gibt die KW-Woche zurück
            KW = DatePart(DateInterval.WeekOfYear, Date.Today(), FirstDayOfWeek.Monday, FirstWeekOfYear.FirstFourDays)



            Try
                Me.MitDBVerbindenBox2()

                If WertListBox2 > 0 Then
                    'Wert in Datenbank schreiben
                    EffektivWert = Convert.ToDouble(WertListBox2.Replace(".", ","))

                    Spannung = 230
                    Leistung = EffektivWert * Spannung

                    'EffektivWert.ToString.Replace(",", ".")weil Mysql akzeptiert nur Punkte(z.B.12.345) und VB nur Kommas(z.B. 12,345)

                    MyCommandBox2 = New MySqlCommand("INSERT INTO boxwerte (strom,spannung,leistung,tag,monat,jahr,KW) VALUES ('" & _
                                                 EffektivWert.ToString.Replace(",", ".") & "','" & Spannung & "','" & Leistung.ToString.Replace(",", ".") & "','" & _
                                                 Date.Now.Day & "','" & Date.Now.Month & "','" & Date.Now.Year & "','" & KW & "');", MyConnectionBox2)

                    MyCommandBox2.ExecuteNonQuery()


                End If


                Me.DBVerbindungTrennenBox2()
            Catch ex As Exception
                lbl_FehlermeldungBox2.Text = "4. Fehler: " & ex.Message
            End Try
        End Sub
        'Datenbank Verbindung aufbauen Box1
        Public Sub MitDBVerbindenBox1()

            Try
                MyConnectionBox1 = New MySqlConnection
                MyConnectionBox1.ConnectionString = "server=127.0.0.1;" & _
                 "UID=root;" & _
                 "pwd=test;" & _
                   "database=Box1;"

                MyConnectionBox1.Open()
            Catch ex As Exception
                lbl_FehlermeldungenAllgemein.Text = "5.Fehler: " & ex.Message
            End Try


        End Sub
        'Datenbank Verbindung aufbauen Box2
        Public Sub MitDBVerbindenBox2()

            Try
                MyConnectionBox2 = New MySqlConnection
                MyConnectionBox2.ConnectionString = "server=127.0.0.1;" & _
                 "UID=root;" & _
                 "pwd=test;" & _
                   "database=Box2;"

                MyConnectionBox2.Open()
            Catch ex As Exception
                lbl_FehlermeldungenAllgemein.Text = "5.Fehler: " & ex.Message
            End Try


        End Sub
        'Datenbank Verbindung trennen
        Public Sub DBVerbindungTrennenBox1()
            Try

                MyConnectionBox1.Close()

            Catch ex As Exception
                lbl_FehlermeldungenAllgemein.Text = "6.Fehler: " & ex.Message
            End Try

        End Sub
        'Datenbank Verbindung trennen
        Public Sub DBVerbindungTrennenBox2()
            Try

                MyConnectionBox2.Close()

            Catch ex As Exception
                lbl_FehlermeldungenAllgemein.Text = "6.Fehler: " & ex.Message
            End Try

        End Sub
        'Box 1 einschalten
        Private Sub bt_Box1Einschalten_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bt_Box1Einschalten.Click

            Try
                Me.MitDBVerbindenBox1()
                If SerialPort1.IsOpen Then
                    SerialPort1.Write(ChrW(32))
                End If

                MyCommandBox1 = New MySqlCommand("UPDATE boxzustand SET zustand=1 WHERE id=1;", MyConnectionBox1)

                'ExecuteNonQuery führt Befehl aus ohne Rückgabewert
                MyCommandBox1.ExecuteNonQuery()


                Me.DBVerbindungTrennenBox1()
            Catch ex As Exception
                lbl_FehlermeldungBox1.Text = "7. Fehler: " & ex.Message
            End Try


        End Sub
        'Box 1 auschalten
        Private Sub bt_Box1Ausschalten_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bt_Box1Ausschalten.Click

            Try
                Me.MitDBVerbindenBox1()
                If SerialPort1.IsOpen Then
                    SerialPort1.Write(ChrW(33))
                End If

                MyCommandBox1 = New MySqlCommand("UPDATE boxzustand SET zustand=0 WHERE id=1;", MyConnectionBox1)

                MyCommandBox1.ExecuteNonQuery()

                Me.DBVerbindungTrennenBox1()
            Catch ex As Exception
                lbl_FehlermeldungBox1.Text = "8. Fehler: " & ex.Message
            End Try
        End Sub
        'Box 2 einschalten
        Private Sub bt_Box2Ein_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bt_Box2Ein.Click
            Try
                Me.MitDBVerbindenBox2()
                If SerialPort1.IsOpen Then
                    SerialPort1.Write(ChrW(34))
                End If

                MyCommandBox2 = New MySqlCommand("UPDATE boxzustand SET zustand=1 WHERE id=1;", MyConnectionBox2)

                MyCommandBox2.ExecuteNonQuery()

                Me.DBVerbindungTrennenBox2()
            Catch ex As Exception
                lbl_FehlermeldungBox2.Text = "9. Fehler: " & ex.Message
            End Try
        End Sub
        'Box 2 auschalten
        Private Sub bt_Box2Aus_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bt_Box2Aus.Click
            Try
                Me.MitDBVerbindenBox2()
                If SerialPort1.IsOpen Then
                    SerialPort1.Write(ChrW(35))
                End If

                MyCommandBox2 = New MySqlCommand("UPDATE boxzustand SET zustand=0 WHERE id=1;", MyConnectionBox2)

                MyCommandBox2.ExecuteNonQuery()

                Me.DBVerbindungTrennenBox2()
            Catch ex As Exception
                lbl_FehlermeldungBox2.Text = "10. Fehler: " & ex.Message
            End Try
        End Sub
        'Zustand Box 1 einlesen und RadioButtons dementsprechend schalten
        Private Function getZustandBox1() As Integer
            Static Zustand1 As Integer


            Try
                Me.MitDBVerbindenBox1()

                MyCommandBox1 = New MySqlCommand("SELECT zustand FROM boxzustand WHERE id=1;", MyConnectionBox1)

                'ExecuteScalar führt den Befehl aus mit einem Rückgabewert
                Zustand1 = System.Convert.ToInt16(MyCommandBox1.ExecuteScalar())

                Me.DBVerbindungTrennenBox1()

                If Zustand1 = 1 Then

                    Return 1
                End If

            Catch ex As Exception
                lbl_FehlermeldungBox1.Text = "11. Fehler: " & ex.Message
            End Try
            Return 0


        End Function
        'Zustand Box 2 einlesen
        Private Function getZustandBox2() As Integer
            Static Zustand2 As Integer

            Try
                Me.MitDBVerbindenBox2()

                MyCommandBox2 = New MySqlCommand("SELECT zustand FROM boxzustand WHERE id=1;", MyConnectionBox2)

                'ExecuteScalar führt den Befehl aus mit einem Rückgabewert
                Zustand2 = Convert.ToInt16(MyCommandBox2.ExecuteScalar())
                Me.DBVerbindungTrennenBox2()

                If Zustand2 = 1 Then

                    Return 1
                End If




            Catch ex As Exception
                lbl_FehlermeldungBox2.Text = "12. Fehler: " & ex.Message
            End Try
            Return 0


        End Function
        'Zustände schalten mit Button von der Homepage über Datenbank
        Private Sub ZustandBox1Box2()

            If Me.getZustandBox1 = 1 Then
                'Box 1 einschalten
                If SerialPort1.IsOpen Then
                    SerialPort1.Write(ChrW(32))
                End If
            Else
                'Box 1 ausschalten
                If SerialPort1.IsOpen Then
                    SerialPort1.Write(ChrW(33))
                End If
            End If


            If Me.getZustandBox2 = 1 Then
                'Box 2 einschalten
                If SerialPort1.IsOpen Then
                    SerialPort1.Write(ChrW(34))
                End If

            Else
                'Box 2 ausschalten
                If SerialPort1.IsOpen Then
                    SerialPort1.Write(ChrW(35))
                End If

            End If

          

        End Sub

       
    End Class

     

    Verwende MySQL Server 5.0 und MySQL Connector Net 6.2.4

     

     

     


    Mittwoch, 20. April 2011 11:30
  • Hallo _Tim,

    ich habe Deinen Thread nicht vergessen, habe aber leider meinen Rechnre mit VB gerade am updaten. Melde mich nachher nochmal. Du solltest Dir aber auf jeden Fall einmal über eine parameterisierte Datenbankabfrage nachdenken ( link: http://www.vbmysql.com/articles/vbnet-mysql-tutorials/the-vbnet-mysql-tutorial-part-4 ). Du sicherst damit Deinen Code gegen SQL injection ( link: http://de.wikipedia.org/wiki/SQL-Injection ) ab und musst Dich nicht mehr um die Kommas ('EffektivWert.ToString.Replace(",", ".")) kümmern.

    Gruß


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Mittwoch, 20. April 2011 11:58
  • Bin wieder da ;-)

    Ich habe mir Deinen Code mal angeschaut und für mich und meine DB mal getestet, ob das Problem an der DB liegt. Ich habe in einer Schleife ( mit 500 Durchläufen ) jeweils 2 MySQLConnections geöffnet und jeweils ( pro connection ) eine SELECT Anweisung durchgeschickt in der gleichen Art wie Du, also Connection.Open(), Command.ExecuteScalar(), Connection.Close() und dabei sind keine Probleme aufgetreten. Lass Dir doch mal den Stacktrace der exception evetl. mit innerexception mit ausgeben um genau zu sehen, wo/was die Exception ausgelöst hat.

    Ich habe das mit MySQL 5 und Connector 6.3.2 getestet.

     

    Gruß


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Mittwoch, 20. April 2011 12:41
  • OK Danke!

    werde den stacktrace mal mitausgeben und melde mich dann nochmal...

    Mittwoch, 20. April 2011 13:18
  • Noch etwas, setze mal ganz oben in Deinem code Fenster Option Strict On:

    Option Strict On
    Public Class ...
    

     

    dann werden Dir noch ein paar Probleme offenbart.


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Mittwoch, 20. April 2011 13:21
  • Ohne die Details zu kennen, würde ich erst einmal asynchrone Ereignisse
    verantwortlich machen, die dazu führen, dass die Datenbank gleichzeitig von
    2 parallelen (asynchronen) threads angesprochen wird. Versuche mal die zu
    speichernden Daten in eine Queue einzureihen und die Elemente der Queue in
    nur einem threads sequentiell abzuarbeiten.
     
    --
    Viele Grüße
    Peter
     
     
    Mittwoch, 20. April 2011 17:42

  • @Peter

    Danke für deinen Tipp, aber ich habe leider noch nicht die nötige Programmierkenntnisse um deinen Tipp umsetzen zu können

    @Heslacher

    So habe jetzt nochmal getestet mit ausgabe folgender Fehlermeldung an den Problemstellen im Programm:

    z.B.

    lbl_FehlermeldungBox2.Text = "13. Fehler: " & ex.Source & vbNewLine & ex.Message & "Fehlerzeit " & Now

    Fehlerbericht:

    Fehler: nach ca. 295 Messwerten in Function getZustandBox2
    Connenction must be valid and open (ex.source=MySql.Data)
    Ausnahme des Typs "System.InvalidOperationException" ist in MySql.Data.dll aufgetreten

    Fehler: nach ca. 560 Messwerten in Sub WerteInDBEintragenBox1()
    Connenction must be valid and open (ex.source=MySql.Data)
    Ausnahme des Typs "System.InvalidOperationException" ist in MySql.Data.dll aufgetreten

    Fehler: nach ca. 1092 Messwerten in Function getZustandBox1
    Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt
    Ausnahme des Typs "System.NullReferenceException" ist in MySql.Data.dll aufgetreten

    Beendung des Tests bei ca 1300 Messwerten

    Trotz Fehlermeldungen wurde weiter eingetragen und ausgelesen. Werde die Fehlermeldungen halt so hin nehmen, bis ich die Lösungen für die Fehler habe.

    Werde jetzt mal testen wie es läuft wenn jetzt auch noch zusätzlich die ASP.NET homepage auf die Datenbank zugreift und die Werte ausliest, die das VB.NET Programm eingetragen hat.

    Gibt es eigentlich eine zeitliche Begrenzung in bezug auf zugriffe auf eine MySql-Datenbank?? Das man sagt Verbindungsaufbau- und beenden nimmt mind. so und so viel Zeit in Anspruch.

    Gruß
    Donnerstag, 21. April 2011 10:41
  • Hallo Tim,

    gib doch mal Deine Fehler so aus:

      

    lbl_FehlermeldungBox2.Text = "13. Fehler: " & ex.Source & vbNewLine & ex.StackTrace & "Fehlerzeit " & Now
    
    
    

    dann sieht man wo genau der Fehler aufgetreten ist ( in welcher Zeile ).
    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Donnerstag, 21. April 2011 10:55
  • ein paar Tests weiter...

    13.Fehler: Fehlerquelle MySql.Data
    Zeile:    bei MySql.Data.MySqlClient.MySqlCommand.CheckState()
       bei MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
       bei MySql.Data.MySqlClient.MySqlCommand.ExecuteScalar()
       bei BackEndProzess.Form1.getZustandBox2() in F:\BackEndProzess_V2\BackEndProzess\Form1.vb:Zeile 330.

    INFO: Zeile 330 --> getZustandBox2 = Convert.ToInt16(MyCommandBox2.ExecuteScalar())
     
    12.Fehler: Fehlerquelle MySql.Data
    Zeile:    bei MySql.Data.MySqlClient.MySqlCommand.CheckState()
       bei MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(CommandBehavior behavior)
       bei MySql.Data.MySqlClient.MySqlCommand.ExecuteScalar()
       bei BackEndProzess.Form1.getZustandBox1() in F:\BackEndProzess_V2\BackEndProzess\Form1.vb:Zeile 311.
       
    INFO: Zeile 311 --> getZustandBox1 = System.Convert.ToInt16(MyCommandBox1.ExecuteScalar())
     
    6.Fehler: Fehlerquelle MySql.Data
    Zeile:    bei MySql.Data.MySqlClient.MySqlDataReader.Close()
       bei MySql.Data.MySqlClient.MySqlConnection.Close()
       bei BackEndProzess.Form1.DBVerbindungTrennenBox1() in F:\BackEndProzess_V2\BackEndProzess\Form1.vb:Zeile 210.
    Fehlermeldung: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
    Fehlerzeit 21.04.2011 14:03:19

    INFO: Zeile 210 --> MyConnectionBox1.Close()

    Das einzig positive an der Sache ist, dass die Fehler immer die gleichen sind und immmer an der gleichen Stelle auftreten...

    Ist es ein problem vom Code her oder von den Datentypen??  

    Vielleicht sollte ich das auslesen mal mit MyCommandBox1.ExecuteReader versuche? Ist aber eigentlich nicht sinnvoll, da ich an dieser Stelle ja nur einen Wert auslesen will - nämlich den Zustand, der nur zwei Werte an nehmen kann 0 oder 1.     

    Gruß

     

    Donnerstag, 21. April 2011 12:16
  • Sehe ich es richtig, dass Du bei der Methode bt_Box1Einschalten_Click() an Deinen SerialPort ein Kommando sendest, so dass wieder Daten vom SerialPort kommen sollen ?


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Donnerstag, 21. April 2011 13:21
  • Die Idee ist ganz einfach.
     
    Es gibt eine Queue in der Klasse. Die Ereignisroutine, die die Daten empfängt, ist so aufgebaut, dass sie außer der Variablen für die Queue keine weiteren außerhalb der Routine deklarierten Variablen nutzt. Damit wird die Routine parallel verwendbar. Wenn die Ereignisse sehr kurz hintereinander kommen, dann wird die Routine mehrmals parallel abgearbeitet. Die Routine baut aus jedem Ereignis ein Objekte, welches in die Queue eingereiht. Zusätzlich wird gleichzeitig ein Ereignis an die Verarbeitungsroutine gesendet.
     
    Die Verarbeitungsroutine wartet auf das Ereignis. Wenn das eintrifft, dann wird aus der Queue ein Objekt entnommen und verarbeitet. Am Ende der Verarbeitung eines Objekt wird das nächste Objekt aus der Queue entnommen und verarbeitet. Wenn die Queue leer ist, wird wieder auf das Ereignis gewartet.
     
    --
    Viele Grüße
    Peter
    Donnerstag, 21. April 2011 19:25
  • Hallo,

    Du hast generell ein Problem mit threadübergreifenden Zugriffen.
    Das SerialPort.DataReceived Ereignis wird auf einem sekundären (ThreadPool) Thread aufgerufen.
    Das dürfte Dir auch aufgefallen sein, denn damit sind auch direkte Zugriffe Steuerelemente tabu,
    siehe Gewusst wie: Threadsicheres Aufrufen von Windows Forms-Steuerelementen

    Deine Lösung CheckForIllegalCrossThreadCalls einfach abzuwürgen rächt sich dann nur an anderer Stelle:

    Denn Deine Funktionen wie getZustandBox1 können so parallel von zwei Threads aufgerufen werden,
    einmal vom Windows Timer (der läuft auf dem UI-Thread) und noch mal via DataReceived auf einem
    Hintergrundthread.

    Neben den Ausnahmen kannst Du musst mit fehlerhaften Rückgaben/Setzen rechnen,
    denn Deine Zustand(n) Variablen können so auch mal "lügen".
    Und Datenbank-Klassen (MySqlConnection und Co.) sollte man niemals über mehrere Threads hinweg verwenden.

    Eine simple Lösung ohne größeren Umbau gibt es, soweit ich das bisher sehe nicht.
    Denn würdest Du die Zugriffe via SyncLock kapseln, was minimal notwendig wäre,
    wird Dir durch unverhersagbare Überschneidungen von Timer/DataReceived Thread
    über kurz oder lang eine Blockierung oder Race Condition um die Ohren fliegen.

    Peter Fleischers Vorschlag ist insofern der richtige Ansatz, ohne einen größeren Umbau wird
    es dabei aber nicht abgehen. (Und zuvor solltest Du Hannes Vorschlag folgen und Deinen
    Code für Option Strict On tauglich machen).

    Ein Beispiel wie eine solche Queue aussehen kann, hatte ich mal in den VB-Newsgroups gegeben:
    http://www.ms-news.net/f827/event-aus-einer-klasse-soll-auf-form-etwas-ausfuehren-9268354.html
    (das dortige Terminal wäre Deinem SerialPort sehr ähnlich).

    Gruß Elmar

    Freitag, 22. April 2011 10:51
    Beantworter
  • Noch ein kleines Beispiel, wie Du Deinen Code besser kapselst und lesbarer machst:

    'Visual Basic 2008 - .net 3.5 - Any CPU
     'Zustand je nach Datenbank auslesen und zurückgeben
     Private Function getZustand(ByVal Databasename As String) As Integer
     Dim ReturnValue As Integer
     Try
      Using myConnection As New MySql.Data.MySqlClient.MySqlConnection(String.Concat(TheConnectionString, Databasename, ";"))
      myConnection.Open()
      Using mycommand As New MySql.Data.MySqlClient.MySqlCommand("SELECT zustand FROM boxzustand WHERE id=1;", myConnection)
       Try
       ReturnValue = Convert.ToInt32(mycommand.ExecuteScalar())
       Catch ex As Exception
       Throw ex
       Finally
       myConnection.Close()
       End Try
      End Using
      End Using
     Catch ex As Exception
      Throw ex
     End Try
     Return ReturnValue
     End Function
     'Box je nach Datenbank und Zustand updaten ( also ein oder ausschalten )
     Private Sub UpdateBox(ByVal Databasename As String, ByVal Value As Integer)
     Try
      Using myConnection As New MySql.Data.MySqlClient.MySqlConnection(String.Concat(TheConnectionString, Databasename, ";"))
      myConnection.Open()
      Using mycommand As New MySql.Data.MySqlClient.MySqlCommand("UPDATE boxzustand SET zustand=@zustand WHERE id=1;", myConnection)
       mycommand.Parameters.AddWithValue("@zustand", Value)
       Try
       mycommand.ExecuteNonQuery()
       Catch ex As Exception
       Throw ex
       Finally
       myConnection.Close()
       End Try
      End Using
      End Using
     Catch ex As Exception
      Throw ex
     End Try
     End Sub
    

     



    Noch ein paar erklärende Links:

    Using keyword, Finally, String.Concat()


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/

    Dienstag, 26. April 2011 06:11
  • Sorry für die formatierung, aber der Code Editor macht mal wieder was er will.
    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Dienstag, 26. April 2011 06:15
  • Vielen dank für die Hilfe!

    Werde mir das alles mal genauer anschauen und versuchen zu verstehen um es in zukunft besser zu machen :-).

    Die vielen nützlichen Links werden mir dabei ja helfen... :-)

     

    @Heslacher

    Finde ich, für mich als Anfänger, sehr sehr hilfreich, dass du auch mal code mit einbringst. Das hilft ungemein zu den zusätzlichen Links!

     

    Gruß

     



    Dienstag, 26. April 2011 07:59