DataSet.ReadXML beschleunigen? oder Hinweis anzeigen?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 26 Antworten in diesem Thema. Der letzte Beitrag () ist von MichaHo.

    DataSet.ReadXML beschleunigen? oder Hinweis anzeigen?

    Hi,
    muss leider noch einmal fragen.
    Mein Programm besteht aus einem Dienst und einer Windows Form. Der Dienst läuft auf einem Server im Netzwerk und fragt alle 15 Minuten Werte eines TemperaturServers ab und speichert diese in ein Dataset. DAS funktioniert bestens.
    Die Windows Form kann von jedem Rechner im Netzwerk aus gestartet werden und soll lediglich die erfassten Daten anzeigen. Funktioniert auch bestens.
    Allerdings kommen bei de Erfassung übers Jahr natürlich nen Haufen Daten zusammen (aktuell über 50.000 Einträge).
    Da das Dataset direkt beim Starten der Form geladen wird, dauert es natürlich ein wenig, bis die Form dann angezeigt wird.
    Ich habe jetzt ne Menge hin und her versucht, einen Hinweis dem User anzuzeigen oder eine Progressbar oder SplashScreen, leider scheint dies in Verbindung mit dem Lesen aus der XML nicht zu funktionieren.
    Beim Debuggen erhalte ich folgende Fehlermeldung im Direktfenster wenn die Form startet: Ausnahme ausgelöst: "System.InvalidOperationException" in System.Windows.Forms.dll

    Ich habe versucht: BackgroundWorker, Task.Run. Aktuell habe ich einen SplashScreen hinzugefügt. Nachdem dies auch nicht funktioniert habe ich meinen SplashScreenCode komplett durch den von @ErfinderDesRades ersetzt -> Splash-Screen mit Status-Meldungen
    der Screen wird angezeigt, aber es tut sich einfach nichts.

    Lasse ich den ganzen Quatsch weg und starte die Form normal und lese die Daten ein, klappt alles bestens nur eben das die Form erst angezeigt wird, wenn das Dataset gefüllt ist.
    Was kann ich noch tun um das Starten zu beschleunigen und/oder dem User anzuzeigen das da noch was in Arbeit ist.
    Danke Euch
    "Hier könnte Ihre Werbung stehen..."
    Hilft dir vllt. Async/Await/Task weiter? oder war das mit

    MichaHo schrieb:

    Task.Run
    gemeint?

    Async, Await und Task
    Wenn das Leben wirklich nur aus Nullen und Einsen besteht, dann laufen sicherlich genügen Nullen frei herum. :D
    Signature-Move 8o
    kein Problem mit privaten Konversationen zu Thema XY :thumbup:
    Wie genau hast du versuch das DataSet im Hintergrund zu laden?

    Ich würde den Ansatz nehmen, einen generischen Task zu erstellen, der dir ein DataSet liefert. In dem Task lädst du die (persistierten) Daten und speicherst sie im DataSet. Das returnest du dann am Ende und wenn der Task fertig ist, weitst du dem UI DataSet die Instanz des Tasks zu.

    Oder du machst es einfacher, indem du in dem Task lediglich die Daten ausliest und in einer Klasse speicherst. Wenn die Daten geladen sind, dann weist du einfach der DataSource Eigenschaft des DataSets das Ergebnis des Tasks zu.

    Lg Radiantor
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Puhhh... hast du ein einfaches Beispiel?

    Meine Sub zum Laden des Dataset sieht so aus:

    VB.NET-Quellcode

    1. Private Sub LoadDataSet(ByVal myDS As DataSet, myFile As FileInfo)
    2. myDS.Clear()
    3. If myFile.Exists Then
    4. myDS.ReadXml(myFile.FullName)
    5. End If
    6. lblFile.Text = myFile.Name
    7. lblValueCount.Text = bsMessung.Count.ToString
    8. End Sub
    "Hier könnte Ihre Werbung stehen..."
    Datasets im Hintergrund bzw. über einen separaten Task zu befüllen habe ich noch nie versucht, allerdings habe ich auch dein Zeitproblem nicht.
    Mir scheint der Grund der Verzögerung nicht beim Laden selbst zu liegen, sondern möglicherweise liegt es ja auch an den Controlls.

    Hilft Dir das hier weiter?

    Eine andere Möglichkeit, mit der ich weit mehr Daten in "Null komma nichts" per Dataset.ReadXml lade ist, in dem ich vor dem Laden alle Bindingsourcen per

    C#-Quellcode

    1. bsXxxxxx.DataMember = null;
    2. bsXxxxxx.DataSource = null;

    entferne und anschliessend wieder neu setze. Auch damit werden die gebundenen Winform.Steuerelemente entlastet.

    Vielleicht hilft Dir ja eine dieser Varianten weiter. Wie es bei anderen Anwendungstypen (WPF, Web) ausschaut weiß ich definitiv nicht.
    ... oder aber Du verwendest die BindingSource.SuspendBinding-Methode
    Das funktioniert jedoch nur bei einfacher Bindung. Bei komplexerer Datenbindung könnte BindingSource.RaiseListChangedEvents-Eigenschaft hilfreich sein.

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

    Hallo,
    leider funktioniert dies auch nicht.
    Weder bsMessung.DataMember = "" und bsMessung.Datasource = Nothing noch bsMessung.SuspendBinding()
    Ich habe auch schon geschaut, ob es eine Asynchrone Variante von ReadXML gibt, gibts aber leider nicht :( also nutzt mir dann Async und Await auch nichts...

    VB.NET-Quellcode

    1. Private Sub LoadDataSet(ByVal myDS As DataSet, myFile As FileInfo)
    2. myDS.Clear()
    3. bsMessung.SuspendBinding()
    4. If myFile.Exists Then
    5. myDS.ReadXml(myFile.FullName)
    6. End If
    7. End Sub

    Aufruf:

    VB.NET-Quellcode

    1. Private Sub btnReload_Click(sender As Object, e As EventArgs) Handles btnReload.Click
    2. LoadDataSet(Dts, _DataFile)
    3. End Sub

    ich bekomme es nicht hin.
    "Hier könnte Ihre Werbung stehen..."
    Ein Workaround könnte sein, einen Splash-Screen anzuzeigen. Im Form.Load Event dann das Dataset füllen. Splash-Screen dann schliessen.
    Ansonsten ist ein funktionierender Hack ist hier zu lesen: Why Can I Not Suspend Binding on a BindingSource, letzter Post.
    @Dksksm: nein, leider auch nicht. Die Form bleibt trotzdem hängen bis das Dataset geladen ist.

    @us4711: das mit dem Splash Screen hatte ich ja schon, siehe Post #1. Ich versuch es aber noch einmal, diesmal ohne Progressbar (oder auf Marquee gestellt).
    "Hier könnte Ihre Werbung stehen..."
    So, ich hab jetzt den Splashscreen wieder eingestellt und die Progressbar auf Marquee gestellt.

    der Hauptform habe ich dann noch ein BackgroundImage gegeben, das den User anzeigt das Daten geladen werden.

    meine LoadDataSet sieht nun so aus:

    VB.NET-Quellcode

    1. Private Sub LoadDataSet(ByVal myDS As DataSet, myFile As FileInfo)
    2. dgvMessung.Visible = False
    3. myDS.Clear()
    4. If myFile.Exists Then
    5. Me.SuspendLayout()
    6. myDS.ReadXml(myFile.FullName)
    7. Me.ResumeLayout()
    8. End If
    9. lblFile.Text = myFile.Name
    10. lblValueCount.Text = String.Format("{0:0,0}", bsMessung.Count)
    11. bsMessung.Sort = "ID"
    12. dgvMessung.Visible = True
    13. End Sub


    Soweit funktioniert das alles gut, bis auf die Tatsache das das dgv NICHT ausgeblendet wird. :(
    Hab auch versucht das DGV beim Aufruf der LoadDataSet Sub auszublenden und hinter wieder einzublenden, aber das will er auch nicht. Das DGV bleibt angezeigt...
    Hat dazu noch einer ne Idee?
    "Hier könnte Ihre Werbung stehen..."
    lass die visible Sachen für das DGV mal komplett aus dem Sub draußen und kläre beides - visible true und visible false - außerhalb des Subs beim Aufruf

    VB.NET-Quellcode

    1. dgv.visible = false
    2. LoadDataSet(...)
    3. dgv.visible =true
    Wenn das Leben wirklich nur aus Nullen und Einsen besteht, dann laufen sicherlich genügen Nullen frei herum. :D
    Signature-Move 8o
    kein Problem mit privaten Konversationen zu Thema XY :thumbup:
    @Acr0most: hab ich auch schon versucht, leider ohne Erfolg :(
    Hab auch ein Backup zurück gespielt, den Code noch einmal angepasst und Projektmappe neu erstellt.
    Trotzdem bleibt das DGV da wo es ist...
    Der Witz ist, in einem neuen Projekt funktioniert das... ABER nur solange, bis ich ein BackgroundImage einrichte. Sobald ich dies einrichte funktioniert es nicht mehr, auch nicht, wenn ich es wieder lösche...
    Ich kapiers nicht
    "Hier könnte Ihre Werbung stehen..."
    die Rufe ich beim Laden, beim Button Reload und beim Button Open auf.
    Hier mal der komplette Code der Klasse:

    VB.NET-Quellcode

    1. #Region "FileHeader"
    2. #If False Then
    3. Oberfläche zur Anzeige der erfassten Messwerte
    4. #End If
    5. Imports System.ComponentModel
    6. Imports System.Globalization
    7. Imports System.IO
    8. Imports WinApplication.dsMNServer
    9. #End Region
    10. Public Class frmMain
    11. Private _DataDate As String = Date.Now.Year.ToString
    12. Private _DataPath As String = "\\hq-dp01\Daten\mnKK"
    13. Private _DataFile As New FileInfo(_DataPath & "\dsTemp." & _DataDate)
    14. Private _Splash As frmSplash
    15. Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    16. If _Splash Is Nothing Then
    17. _Splash = Application.OpenForms.OfType(Of frmSplash).FirstOrDefault
    18. LoadDataSet(Dts, _DataFile)
    19. End If
    20. End Sub
    21. Private Sub btnReload_Click(sender As Object, e As EventArgs) Handles btnReload.Click
    22. dgvMessung.Visible = False
    23. LoadDataSet(Dts, _DataFile)
    24. dgvMessung.Visible = True
    25. End Sub
    26. Private Sub btnOpen_Click(sender As Object, e As EventArgs) Handles btnOpen.Click
    27. Dim ofd As New OpenFileDialog With {.Filter = "All files (*.*)|*.*", .InitialDirectory = "\\hq-dp01\Daten\mnKK\"}
    28. If ofd.ShowDialog <> DialogResult.OK Then Return
    29. Dim _OldFile As New FileInfo(ofd.FileName)
    30. dgvMessung.Visible = False
    31. LoadDataSet(Dts, _OldFile)
    32. dgvMessung.Visible = True
    33. End Sub
    34. Private Sub LoadDataSet(ByVal myDS As DataSet, myFile As FileInfo)
    35. myDS.Clear()
    36. If myFile.Exists Then
    37. Me.SuspendLayout()
    38. myDS.ReadXml(myFile.FullName)
    39. Me.ResumeLayout()
    40. End If
    41. lblFile.Text = myFile.Name
    42. lblValueCount.Text = String.Format("{0:0,0}", bsMessung.Count)
    43. bsMessung.Sort = "ID"
    44. End Sub
    45. End Class
    "Hier könnte Ihre Werbung stehen..."
    und wo genau soll ich es einbauen?
    Im btnLoad funktioniert es nicht

    EDIT:
    Versuche gerade, die LoadDataSet Sub in einen Async sub zu verwandeln. Leider reicht mein Wissen nicht aus dafür.
    Ich hab es so versucht (verursacht aber leider einen Ausnahmefehler in der Delegate.cs):

    VB.NET-Quellcode

    1. Private Async Sub LoadDS(myDS As DataSet, myFile As FileInfo)
    2. Dim xmlSettings As New XmlReaderSettings() With {.Async = True}
    3. myDS.Clear()
    4. Using stream = myFile.Open(FileMode.Open), reader = XmlReader.Create(stream, xmlSettings)
    5. While Await reader.ReadAsync()
    6. myDS.ReadXml(Await reader.GetValueAsync())
    7. End While
    8. End Using
    9. End Sub


    EDIT2:
    Denkfehler:

    VB.NET-Quellcode

    1. Private Async Sub LoadDS(myDS As DataSet, myFile As FileInfo)
    2. Dim xmlSettings As New XmlReaderSettings() With {.Async = True}
    3. myDS.Clear()
    4. Using stream = myFile.Open(FileMode.Open), reader = XmlReader.Create(stream, xmlSettings)
    5. While Await reader.ReadAsync()
    6. myDS.ReadXml(reader)
    7. End While
    8. End Using
    9. End Sub

    so wirds eingelesen :) jetzt muss ich nur noch gucken, wie das im Hintergrund läuft, denn noch frierts immernoch ein

    "Hier könnte Ihre Werbung stehen..."

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

    :(
    habs jetzt mal so umgebaut
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class frmMain
    2. Private _DataDate As String = Date.Now.Year.ToString
    3. Private _DataPath As String = "\\hq-dp01\Daten\mnKK"
    4. Private _DataFile As New FileInfo(_DataPath & "\dsTemp." & _DataDate)
    5. Private _Splash As frmSplash
    6. Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    7. If _Splash Is Nothing Then
    8. _Splash = Application.OpenForms.OfType(Of frmSplash).FirstOrDefault
    9. Await LoadDS(Dts, _DataFile)
    10. End If
    11. End Sub
    12. Private Async Sub btnReload_Click(sender As Object, e As EventArgs) Handles btnReload.Click
    13. Await LoadDS(Dts, _DataFile)
    14. End Sub
    15. Private Async Sub btnOpen_Click(sender As Object, e As EventArgs) Handles btnOpen.Click
    16. Dim ofd As New OpenFileDialog With {.Filter = "All files (*.*)|*.*", .InitialDirectory = "\\hq-dp01\Daten\mnKK\"}
    17. If ofd.ShowDialog <> DialogResult.OK Then Return
    18. Dim _OldFile As New FileInfo(ofd.FileName)
    19. Await LoadDS(Dts, _OldFile)
    20. End Sub
    21. Private Async Function LoadDS(myDS As DataSet, myFile As FileInfo) As Task
    22. Dim xmlSettings As New XmlReaderSettings() With {.Async = True}
    23. myDS.Clear()
    24. dgvMessung.Visible = False
    25. Using stream = myFile.Open(FileMode.Open), reader = XmlReader.Create(stream, xmlSettings)
    26. While Await reader.ReadAsync()
    27. myDS.ReadXml(reader)
    28. End While
    29. End Using
    30. lblFile.Text = myFile.Name
    31. lblValueCount.Text = String.Format("{0:0,0}", bsMessung.Count)
    32. bsMessung.Sort = "ID"
    33. dgvMessung.Visible = True
    34. End Function
    35. End Class

    Macht das selbe und tut auch nicht weh :D
    Aber schneller oder gar im Hintergrund läuft da nix und das DGV wird immernoch nicht ausgeblendet ;(

    EDIT:
    ich hänge das Projekt mal dran, vielleicht mag jemand drüber gucken und findet etwas, wo es klemmt.
    Ich habe 2 Dateien mit ins Projekt eingebunden (der Button NeuLaden lädt die Ursprungsdatei, mit Öffnen kann man die andere Datei auswählen).

    Danke schon mal an alle, die rein schauen...
    Dateien
    • TempControl10.zip

      (606,36 kB, 122 mal heruntergeladen, zuletzt: )
    "Hier könnte Ihre Werbung stehen..."

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

    Allein das macht die Sache deutlich flotter.

    VB.NET-Quellcode

    1. DataGridView.Enabled = False
    2. DataSet.ReadXml(xml)
    3. DataGridView.Enabled = True


    Damit habe ich bei ~35000 datensaetze, ~17MB Xml-Dateigroesse folgende Zeiten:
    00:00:02.4240000 'mit disablen
    00:00:05.1160000 'ohne disablen

    Also weniger als die haelfte.
    And i think to myself... what a wonderfuL World!