Datatable vergleichen und neue Einträge rüberkopieren

  • VB.NET

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Datatable vergleichen und neue Einträge rüberkopieren

    Wieder stieß ich auf ein sehr unangenehmes Problem und finde da keine effiziente und erklärende Lösung.

    Ich nutze einfache Datatables, die nicht gebindet werden, sondern aus einer XML Datei heraus geladen werden:

    Form1_Load:

    VB.NET-Quellcode

    1. With dt_liste1
    2. .Columns.Add("Index", GetType(System.Int32))
    3. .Columns.Add("Name", GetType(System.String))
    4. .Columns.Add("Vorname", GetType(System.String))
    5. End With
    6. With dt_liste2
    7. .Columns.Add("Index", GetType(System.Int32))
    8. .Columns.Add("Name", GetType(System.String))
    9. .Columns.Add("Vorname", GetType(System.String))
    10. End With
    11. With DataGridView1
    12. .DataSource = dt_liste1
    13. End With
    14. With DataGridView2
    15. .DataSource = dt_liste2
    16. End With


    So werden die Variablen definiert. In einem Timer wird in die dt_liste 1 eine XML-Datei geladen. Die Collums etc. sind identisch.

    Timer1:

    VB.NET-Quellcode

    1. Dim xmlFile As Xml.XmlReader
    2. xmlFile = Xml.XmlReader.Create(Application.StartupPath + "\" + "daten" + ".xml", New Xml.XmlReaderSettings())
    3. dt_liste1.ReadXml(xmlFile)
    4. xmlFile.Close()


    Soweit so gut: Die Datagridview1, die an die dt_liste1 gebindet ist, lädt die XML Liste wunderbar. Es gibt hier nur zwei Probleme:

    Füge ich folgendes hinzu, passiert "wortwörtlich nichts":

    VB.NET-Quellcode

    1. dt_liste2 = dt_liste1.Copy


    Die Datagridview2 bleibt leer! Mittels MsgBox(dt_liste2.Rows.Count) ist der Rows.count bei 0 obwohl dt_liste1 mit vielen Einträgen gefüllt ist.


    Problemstellung 2:

    Da das in einem Timer laufen soll, will ich quasi, dass die Datagridview, sobald die XML Datei sich geändert hat, automatisch die neu dazugekommenen Einträge zur Datagridview addieren, ohne die DGV vorher komplett zu löschen und neuzufüllen. Mein Ansatz war, dass eine zweite datatable gefüllt wird und verglichen wird mit der dt_liste1, um so die Differenz zu erlangen und sie einfach als .rows.add(..) in die dt_liste1 hinzuzufügen.


    MfG

    Semiconductor schrieb:

    VB.NET-Quellcode

    1. dt_liste2 = dt_liste1.Copy
    verändert den Inhalt der Variable dt_liste2 -> neue Instanz, nicht aber anderer Inhalt. Die DataSource behält jedoch die alte Instanz.
    Wenn die Instanz von dt_liste2 erhalten bleibt, werden die veränderten Daten sofort angezeigt:

    VB.NET-Quellcode

    1. Me.DataGridView2.DataSource = Me.table2
    2. Me.table2.Merge(Me.table)

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Verflixt, das Merge hatte ich auch gelesen gehabt, allerdings dachte ich, dass .Copy die richtigere Variante ist.

    Soweit so gut habe ich zumindest das Kopieren von Datatables untereinander gelöst.

    Gibt es auch die Möglichkeit beim Merge nur die neuen Einträge zu kopieren?

    DT1 hat 10 Einträge
    DT2 hat 15 Einträge, wobei davon 10 Einträge dieselben sind wie von DT1, somit sollen nur die 5 neuen auf DT2 via merging hinzugefügt werden?

    Semiconductor schrieb:

    nur die neuen Einträge
    Da ist es fast sinnvoll, Du verwendest Deine eigene MyDataRow von DataRow abgeleitet und gibst der einen Compare-Operator.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Das habe ich versucht, ein Primekey ist im Form1_Load vergeben worden:

    VB.NET-Quellcode

    1. '''Lade in dt_liste1 die XML Datei
    2. ...
    3. Invoke(Sub() dt_liste2.Merge(dt_liste1))
    4. Invoke(Sub() dt_liste1.Clear())


    Es wird ein "Merge" durchgeführt, allerdings erweitert sich dt_liste2 nochmals doppelt, das heißt, die gesamte Liste von dt_liste wird einfach hinzuaddiert, statt doppelte Einträge nicht eintragen zu lassen :S

    Semiconductor schrieb:

    ein Primekey ist im Form1_Load vergeben worden
    mit welchem Code?
    Ich denke, zum Operieren mit DataTables brauchst Du kein Invoke.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Achso, mein Fehler, falsch rüberkopiert:

    Also in Form1_Load ist es jetzt so konfiguriert:

    VB.NET-Quellcode

    1. With dt_liste1
    2. .Columns.Add("Index", GetType(System.Int32))
    3. .Columns.Add("Name", GetType(System.String))
    4. .Columns.Add("Vorname", GetType(System.String))
    5. .TableName = "Liste"
    6. .PrimaryKey = {dt_liste1.Columns("dt")}
    7. End With



    Probier das mal so:

    VB.NET-Quellcode

    1. Dim keys(0) As DataColumn
    2. keys(0) = dt_liste1.Columns("Index")
    3. dt_liste1.PrimaryKey = keys
    So funktioniert das Merge jetzt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Neuer Edit:

    Ich habe jetzt die Merge Funktion mal intensiv getestet und habe folgende Erkenntnis:

    Merge funktioniert soweit auch im Thread, wenn man Invoke nutzt und alles funktioniert wunderbar.

    Mein Problem liegt bei folgender Tatsache:

    VB.NET-Quellcode

    1. Dim xmlFile As Xml.XmlReader
    2. xmlFile = Xml.XmlReader.Create(Application.StartupPath.tostring + "\datei.xml", New Xml.XmlReaderSettings())
    3. Invoke(Sub() dt_liste1.ReadXml(xmlFile))
    4. xmlFile.Close()


    Sofern ich dies in einen Thread packe, sodass dt_liste immer wieder gefüllt wird, bringt es das Programm zum Absturz in Kombination mit der Primersache. Ist die Implementierung von XML falsch bzw. nicht richtig?


    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Semiconductor“ ()

    Semiconductor schrieb:

    bringt es das Programm zum Absturz
    Mit der Fehlermeldung Threadübergreifender Vorgang?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Nein, keine Fehlermeldungen, der Thread wird laut VB.NET sauber ausgeführt, hier komplett und vereinfacht:

    VB.NET-Quellcode

    1. Dim Liste = New Threading.Thread(AddressOf Me.getlist)
    2. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    3. Liste.start
    4. End Sub
    5. Sub getlist()
    6. Do
    7. Invoke(Sub() dt_liste1.ReadXml(Application.StartupPath.tostring + "\Liste.xml"))
    8. Liste.CurrentThread.Sleep(10000)
    9. Loop
    10. End Sub


    Allerdings funktioniert der obige Code nur dann einwandfrei, wenn der PrimaryKey nicht definiert ist, also wenn ich das untere bsp. nicht implementiere:

    VB.NET-Quellcode

    1. keys(0) = dt_liste1.Columns("Index")
    2. dt_liste1.PrimaryKey = keys


    Mit PrimaryKey stürzt es dann mitten im Thread ab, bzw. es friert ein. :(


    PS: Habs mal ohne XML.Read ausprobiert, sondern mit Rows.add() und da lief es mit PrimaryKey auch problemlos.

    Das XML-Lesen bereitet anscheinend Probleme!

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

    Lass mal das Thread-Zeugs weg, zunächst muss das ganze so laufen.
    Außerdem hast Du von @ bereits den Tipp mit dem FileSystemWatcher bekommen.
    Desweiteren kannst Du bei 10 Sekunden auch einen Forms.Timer verwenden.
    Kläre zunächst ohne Thread, was passiert. Dann sehen wir weiter.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich habe es jetzt ohne Thread ausprobiert, es friert auch. :(

    VB.NET-Quellcode

    1. Dim dt_liste1, dt_liste3 as new Datatable()
    2. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    3. With dt_liste1
    4. .Columns.Add("Index", GetType(System.Int32))
    5. .Columns.Add("Test", GetType(System.String))
    6. End With
    7. With dt_liste3.Columns.Add("Index", GetType(System.Int32)).Columns.Add("Test", GetType(System.String))End With
    8. With DataGridView1
    9. .Datasource = dt_liste3
    10. .RowHeadersVisible = False
    11. .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCellsExceptHeader
    12. .SelectionMode = DataGridViewSelectionMode.FullRowSelect
    13. .BackgroundColor = Color.White
    14. .MultiSelect = True
    15. .AllowUserToResizeColumns = True
    16. .AllowUserToDeleteRows = True
    17. .AllowUserToAddRows = False
    18. End With
    19. keys(0) = dt_liste3.Columns("Index")
    20. dt_liste1.PrimaryKey = keys
    21. End Sub
    22. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    23. dt_liste1.ReadXml(Application.StartupPath.tostring + "\Liste.xml")
    24. End Sub
    25. Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    26. dt_liste3.Merge(dt_liste1)
    27. Label1.Text = DataGridView2.Rows.Count
    28. Me.Text = dt_liste1.Rows.Count
    29. End Sub
    Fehlermeldung bekommen, habs mit kleinem Tabellensatz probiert:

    Quellcode

    1. Ein Ausnahmefehler des Typs "System.Data.ConstraintException" ist in System.Data.dll aufgetreten.Zusätzliche Informationen: Einschränkungen konnten nicht aktiviert werden. Mindestens eine Zeile enthält Werte die die Einschränkungen non-null, unique or foreign-key verletzen.


    XML DATASET:

    XML-Quellcode

    1. <?xml version="1.0" standalone="true"?>
    2. -<NewDataSet>
    3. -<xs:schema xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="" id="NewDataSet">
    4. -<xs:element msdata:UseCurrentLocale="true" msdata:MainDataTable="Liste" msdata:IsDataSet="true" name="NewDataSet">
    5. -<xs:complexType>
    6. -<xs:choice maxOccurs="unbounded" minOccurs="0">
    7. -<xs:element name="Liste">
    8. -<xs:complexType>
    9. -<xs:sequence>
    10. <xs:element name="Index" minOccurs="0" type="xs:int"/>
    11. <xs:element name="Test" minOccurs="0" type="xs:string"/>
    12. </xs:sequence>
    13. </xs:complexType>
    14. </xs:element>
    15. </xs:choice>
    16. </xs:complexType>
    17. </xs:element>
    18. </xs:schema>
    19. -<Liste>
    20. <Index>0</Index>
    21. <Test>TEST</Test>
    22. </Liste>
    23. -<Liste>
    24. <Index>0</Index>
    25. <Test>TEST</Test>
    26. </Liste>
    27. -<Liste>
    28. <Index>0</Index>
    29. <Test>TEST</Test>
    30. </Liste>
    31. </NewDataSet>