DataAdapter.Update und Primärschlüssel

  • C#

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von MasterQ.

    DataAdapter.Update und Primärschlüssel

    Moin,

    ein ​DataAdaper.Update(dataSet) schreibt Änderungen der ​DataDource zurück in die Datenbank. Das geht also nur in eine Richtung. Und wie geht es andersrum?

    Der Nutzer kann in meiner Anwendung neue Datensätze (Zeilen) im DataGridView eintragen und diese werden auch in die DB geschrieben, soll heißen es wird ein ​INSERT ausgeführt. Die DB legt für diesen neuen Datensatz ein Primärschlüssel an, der meinem Progrämmle allerdings nicht automatisch zurückgemeldet wird. Den brauche ich aber direkt danach um damit in einer anderen Tabelle diesen als Fremdschlüssel eintragen zu können.

    Ich habe nun gelesen, man solle das DataAdapter.RowUpdate]-Event abfangen und darin wohl ein ​DataAdapter.Fill(dataSet) ausführen.

    Das klappt nicht, denn ich erhalten eine Exception, da wohl gerade schon ein Kommando (SELECT * FROM Tabelle) ausgeführt würde. ?(

    Wie macht man denn nun ein "Sync" des DataAdapters, also ein Schreiben der Änderungen in die DB und dann anschließen wieder einen Abgleich andersrum? Ich habe gestern viel rumgelesen aber so richtig schlau wurde ich nicht.

    Gruß

    MQ
    Danke für den Hinweis.

    Wenn ich den Anfang richtig verstehe, dann müsste es eigentlich funktionieren, wenn die DB In/Out-Parameter unterstützt.

    Ich verwende PostgreSQL Version 9.6 und da sollten In/Out-Parameter eigentlich implementiert sein.
    Ich habe deinen Code analysiert und du machst das im Prinzip so, wie ich vermutet hatte, dass man das macht.

    Es scheint aber nun so, dass ich in eine Endlosschleife laufe.

    C#-Quellcode

    1. // Event RowChange
    2. private void UpdateDB(object Sender , DataRowChangeEventArgs e) {
    3. if (dataSet.HasChanges()) {
    4. Debug.Print("Event RowChanged");
    5. adapter.Update(dataSet);
    6. toolStripStatusLabel1.Text = dataSet.Tables[0].Rows.Count.ToString() + " Einträge";
    7. }
    8. }


    C#-Quellcode

    1. // Event RowUpdated
    2. private void UpdateDataSet(object sender, NpgsqlRowUpdatedEventArgs e){
    3. Debug.Print("Event RowUpdated");
    4. if ((int)e.StatementType == (int)Npgsql.StatementType.Insert) {
    5. Debug.Print("INSERT");
    6. object key = cmd.ExecuteScalar();
    7. e.Row["RechnungID"] = (Int64)key;
    8. Debug.Print(key.ToString());
    9. }
    10. }


    Nachdem man die Zeile des DataGridView verlässt, wird ein RowChange-Event getriggert und ich mache ein Update. Nach dem Update kommt ein RowUpdated, der neue Primärschlüssel wird gelesen und der entsprechenden Zelle zugewiesen. Dies scheint aber erneut ein RowChange-Event zu schmeißen und das Ganze geht von vorne los.

    Irgendwas übersehe ich da!


    MQ
    ja genau, nach rund 3500 Runden war der Stack übergelaufen und meine Tabelle auf dem Server zugemüllt.

    Es wurde also weiterhin immer wieder der selbe Datensatz eingefügt.

    Das möchte ich ungern wiederholen, obwohl das Reparieren bei PostgresQL wohl relativ einfach von statten geht.

    Den Cast benötige ich. Ich verstehe zwar nicht warum, aber der Compiler meckert.

    C#-Quellcode

    1. if (e.StatementType == StatementType.Insert)


    wird als zweideutig gemeckert, weil nicht klar wäre ob die rechte Seite ein System.Data.StatementType oder ein Npgsql.StatementType wäre.

    Obwohl e als NpgsqlRowUpdatedEventArgs definiert wird, geht IntelliSens und auch der Compiler von System.Data.RowUpdatedEventArgs aus. Bug???

    Also entweder caste ich oder ich muss ein System.Data. davorsetzen. Jacke wie Hose.


    Es bleibt die Frage, warum nach dem Update der RowState nicht zurückgesetzt wird, was zur Endlosschleife führt. Beim zweiten Durchlauf müsste das DataAdapterUpdate eigentlich kein INSERT mehr sondern ein UPDATE ausführen.

    Dass das Zuweisen eines Wertes in eine Zelle ein RowChanged triggert wundert mich eher nicht. Aber dein Code scheint ja zu funktionieren, zumindest wenn man damit an den microsoftigen SQL Server geht.

    Ist hier also was prinzipiell im Argen oder liegt hier noch ein Bug rum?

    Nachtrag:

    Ich habe die Eventroutinen etwas modifiziert

    C#-Quellcode

    1. private void UpdateDB(object Sender , DataRowChangeEventArgs e) {
    2. if (dataSet.HasChanges()) {
    3. Debug.Print("Event RowChanged");
    4. adapter.Update(dataSet);
    5. toolStripStatusLabel1.Text = dataSet.Tables[0].Rows.Count.ToString() + " Einträge";
    6. Debug.Print("Event RowChanged done");
    7. }
    8. }
    9. // Event RowUpdated
    10. private void UpdateDataSet(object sender, NpgsqlRowUpdatedEventArgs e){
    11. Debug.Print("Event RowUpdated");
    12. // if ((int)e.StatementType == (int)Npgsql.StatementType.Insert) {
    13. if (e.StatementType == System.Data.StatementType.Insert) {
    14. Debug.Write("INSERT ... ");
    15. object key = cmd.ExecuteScalar();
    16. e.Row["RechnungID"] = (Int64)key;
    17. Debug.Write(key.ToString());
    18. Debug.WriteLine(" ... done");
    19. }
    20. Debug.Print("Event RowUpdated done");
    21. }



    Die Debug-Ausgabe sieht wie folgt aus:


    Quellcode

    1. "Rechnungen.exe" (CLR v4.0.30319: Rechnungen.exe): "C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Numerics\v4.0_4.0.0.0__b77a5c561934e089\System.Numerics.dll" geladen. Das Laden von Symbolen wurde übersprungen. Das Modul ist optimiert, und die Debugoption "Nur eigenen Code" ist aktiviert.
    2. Event RowChanged
    3. Event RowUpdated
    4. INSERT ... Event RowChanged
    5. Event RowUpdated
    6. INSERT ... Event RowChanged
    7. Event RowUpdated
    8. INSERT ... Event RowChanged
    9. Event RowUpdated
    10. INSERT ... Event RowChanged
    11. Event RowUpdated
    12. INSERT ... Event RowChanged
    13. Event RowUpdated
    14. INSERT ... Event RowChanged
    15. Event RowUpdated
    16. INSERT ... Event RowChanged
    17. Event RowUpdated
    18. INSERT ... Event RowChanged
    19. Event RowUpdated


    Man kann also schön sehen, dass die erste Updated-Event-Routine nie beendet wird und dass die Zuweisung von key in die Zelle ein neues RowChanged auslöst. Ansonsten müsste key ebenfalls angegeben sein.


    Tausche ich die Zuweisung und die DebugAusgabe

    C#-Quellcode

    1. Debug.Write(key.ToString());
    2. e.Row["RechnungID"] = (Int64)key;


    dann gibt es das

    Quellcode

    1. "Rechnungen.exe" (CLR v4.0.30319: Rechnungen.exe): "C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Numerics\v4.0_4.0.0.0__b77a5c561934e089\System.Numerics.dll" geladen. Das Laden von Symbolen wurde übersprungen. Das Modul ist optimiert, und die Debugoption "Nur eigenen Code" ist aktiviert.
    2. Event RowChanged
    3. Event RowUpdated
    4. INSERT ... 673Event RowChanged
    5. Event RowUpdated
    6. INSERT ... 674Event RowChanged
    7. Event RowUpdated
    8. INSERT ... 675Event RowChanged
    9. Event RowUpdated
    10. INSERT ... 676Event RowChanged
    11. Event RowUpdated
    12. INSERT ... 677Event RowChanged
    13. Event RowUpdated
    14. INSERT ... 678Event RowChanged
    15. Event RowUpdated
    16. INSERT ... 679Event RowChanged
    17. Event RowUpdated
    18. INSERT ... 680Event RowChanged
    19. Event RowUpdated
    20. INSERT ... 681Event RowChanged
    21. Event RowUpdated
    22. INSERT ... 682Event RowChanged
    23. Event RowUpdated
    24. INSERT ... 683Event RowChanged
    25. Event RowUpdated
    26. INSERT ... 684Event RowChanged
    27. Event RowUpdated
    28. INSERT ... 685Event RowChanged
    29. Event RowUpdated
    30. INSERT ... 686Event RowChanged
    31. Event RowUpdated
    32. INSERT ... 687Event RowChanged
    33. Event RowUpdated
    34. INSERT ... 688Event RowChanged
    35. Event RowUpdated


    Und nu?

    noch was:

    Obwohl Npgsql selbst ein Npgsql.StatementType anbietet beinhalten die NpgsqlRowUpdatedEventArgs ein System.Data.StatementType.

    Auch komisch

    ====================================================================

    C#-Quellcode

    1. private void UpdateDataSet(object sender, NpgsqlRowUpdatedEventArgs e){
    2. if (e.StatementType == System.Data.StatementType.Insert) {
    3. dataSet.Tables[0].RowChanged -= UpdateDB;
    4. e.Row["RechnungID"] = (Int64)cmd.ExecuteScalar();
    5. dataSet.Tables[0].RowChanged += new DataRowChangeEventHandler(UpdateDB);
    6. }
    7. }

    ====================================================================

    does the Job!!

    Also für den kurzen Moment der Zuweisung des neuen Primärschlüssels in die Datentabelle das DataRowChangedEvent abklemmen!

    Ich habe nun keine Ahnung, ob das State of The Art ist oder nur Gemurkse. Aber es tut, was ich erwarte.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „MasterQ“ ()

    MasterQ schrieb:

    Den Cast benötige ich. Ich verstehe zwar nicht warum, aber der Compiler meckert.
    vermutlich musst du nicht casten, sondern die rechte Seite einfach eindeutig spezifizieren - dass der Datentyp der rechtenSeite zum datentyp der linken Seite passt.
    Entweder korrigierste selber, oder gibst hier die genaue Fehlermeldung wieder (was eiglich immer eine gute Idee ist)
    Der Fehler kann u.U. auch Auswirkung auf den Stacküberlauf haben - aber bin ich nicht sicher



    Jo - solch ist prinzipiell tatsächlich State of the Art - also manchmal verfährt man so.



    Eine neue Vermutung die ich habe ist, dass du das falsche Command verwendest.
    Dein Command heisst cmd, und wenn das das insert-Command ist, ist die Ereigniskette natürlich klar.
    Beim mir heisstes RequeryIdCommand, und ist ein vom Insert-Command ganz verschiedenes.
    Kannst du mal cmd.CommandText ausgeben?

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

    Da ich inzwischen weiß, dass Npgsql zwar einen eigenen StatementType anbietet aber dennoch das "normale" verwendet, habe ich als finale Lösung

    C#-Quellcode

    1. if (e.StatementType == System.Data.StatementType.Insert) {



    Zum Abklemmen des Events habe ich gestern einen für mich eleganteren Weg gefunden

    C#-Quellcode

    1. private void UpdateDB(object Sender , DataRowChangeEventArgs e) {
    2. if (!updating) {
    3. if (dataSet.HasChanges()) {
    4. updating = true;
    5. adapter.Update(dataSet);
    6. updating = false;
    7. toolStripStatusLabel1.Text = dataSet.Tables[0].Rows.Count.ToString() + " Einträge";
    8. }
    9. }
    10. }



    Alles rennt jetzt ohne Probleme.

    Danke
    ich find das grad weniger elegant: es sind mehr Zeilen, und beinhaltet eine weitere Abhängigkeit nach ausserhalb der Methode.

    Ich würde auch nicht bei jeder einzelnen Änderung gleich ein Update in die Datenbank schiessen - ich hab lieber einen Save-Button, der savet, wenn ich saven möchte. Dadurch ist bei mir obige Ereigniskette auch nie aufgetreten.

    Ach, und ich hatte doch nach dem CommandText von cmd gefragt - berichteste mir den noch?

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

    das Prozedere sieht wie folgt aus. Der Nutzer trägt Daten in die Tabelle ein und direkt danach soll ein PDF-Upload in eine andere Tabelle erfolgen können und dazu benötige ich den ersten Primärschlüssel als Fremdschlüssel.

    Stimmt, der Cast ist entbehrlich. Der ist mal rein gekommen weil der Compiler gemeckert hatte. Weiß nicht mehr was da war.

    Und hier der SQL-Code für die Abfrage des zuletzt erzeugten Primärschlüssel bei PostgreSQL:

    C#-Quellcode

    1. cmd = new NpgsqlCommand("SELECT currval('\"tblRechnungen_RechnungID_seq\"')" , conn);


    das doppelte ' und " ist notwendig, also currval( ' " ... " ')

    Gruß

    MQ