Änderungen landen nicht in DB

  • C#
  • .NET (FX) 4.0

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Änderungen landen nicht in DB

    Hallo Freunde,

    ich glaube, ich werde mit diesen typisierten Datasets niemals zurechtkommen :(

    Ich habe mir eine Datenbankstruktur zusammen mit dem Kunden erstellt und daraus dann ein typisiertes Dataset erstellt. So weit, so gut. Mein DatagridView zeigt auch sauber alle (zunächst manuell) erstellen Testdaten an. Dann habe ich eine Detailview dazugebaut mit lauter kleinen Controls darin für die restlichen Daten. Auch hier funktioniert die Anzeige wunderbar.
    Zusätzlich gibt es einen "Änderungen speichern"-Button - an dem gibt es keinen Weg vorbei, der Kunde braucht so einen Knopf. Fragt nicht warum, isso :whistling:

    Nehme ich nun also Änderungen vor in den Detail-Controls (z.B. den Namen ändern), dann sehe ich wie sich im DatagridView der Name ändert. Ein Klick auf den "Änderungen speichern"-Button löst folgenden Code aus (ds = das Dataset, KundenTA = TableAdapter):

    C#-Quellcode

    1. private void BtnSaveKunde_Click(object sender, EventArgs e)
    2. {
    3. KundenTA.Update(ds.Kunden);
    4. if (!ds.Kunden.HasErrors)
    5. {
    6. ds.Kunden.AcceptChanges();
    7. }
    8. else
    9. {
    10. //Error anzeigen
    11. }
    12. }


    Damit sollten meine Änderungen in der Datenbank landen und auch im Dataset sind alle Datensätze "sauber". Wie gesagt, im DatagridView ist alles prima. Beende ich das Programm und starte es neu sind alle Änderungen verschwunden ?( :cursing:
    Spaßenshalber habe ich im FormClosing-Event noch ein bissel Sicherheitscode eingebaut, falls der Kunde das Programm beendet, aber noch Änderungen zu speichern sind:

    C#-Quellcode

    1. private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    2. {
    3. if (ds.HasChanges())
    4. {
    5. DialogResult dr = MessageBox.Show("Änderungen speichern ?", "Änderungen nicht gespeichert", MessageBoxButtons.YesNoCancel);
    6. switch(dr)
    7. {
    8. case DialogResult.No:
    9. ds.Kunden.RejectChanges();
    10. break;
    11. case DialogResult.Yes:
    12. KundenTA.Update(ds.Kunden);
    13. ds.Kunden.AcceptChanges();
    14. break;
    15. case DialogResult.Cancel:
    16. e.Cancel = true;
    17. break;
    18. }
    19. }
    20. }


    Unser Kunde bekommt dann also ne Messagebox, wo er mit JA alles abspeichern kann, mit NEIN die Änderungen verwirft und mit ABBRECHEN das Beenden abbricht. So zumindest die Theorie, denn wenn ich (wie zuvor) den Namen ändere, sehe ich das im DatagridView. Mein FormClosing-Handler allerdings zeigt mir keine Meldung an, wenn ich das Programm beenden will. Die Eingabefelder sind allesamt an die kundenBindingSource geklemmt, im Falle des Namens also an kundenBindungSource - Name.

    Ich möchte echt gern den Kriegsfuß, mit dem ich auf diesen Datasets stehe, loswerden - was mache ich denn nun wieder falsch ? :/
    Jo, vb1963 hat die Lösung deines angefragten Problems bereits genannt.

    Ich hab nur noch kleine Code-Verbesserung:

    C#-Quellcode

    1. private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
    2. if (ds.HasChanges()) {
    3. switch (MessageBox.Show("Änderungen speichern ?", "Änderungen nicht gespeichert", MessageBoxButtons.YesNoCancel)) {
    4. case DialogResult.No:
    5. break;
    6. case DialogResult.Yes:
    7. KundenTA.Update(ds.Kunden);
    8. break;
    9. case DialogResult.Cancel:
    10. e.Cancel = true;
    11. break;
    12. }
    13. }
    14. }
    merke: Niemals Dataset.AcceptChanges oder .RejectChanges aufrufen! Diese Methoden sind internen Aufrufen seitens der DataAdapter vorbehalten.
    Ausnahmen sind denkbar, aber dafür sollteste über fundierte fortgeschrittene Kenntnisse verfügen - ansonsten passiert bei diesen Methoden so gut wie immer was anderes, als der Programmierer denkt.



    Du hast allerdings noch mehr Probleme, denn vermutlich weiß dein Main-Form nichts davon, wenn der Detail-Dialog sein Dataset abspeichert.
    Das ist nämlich ein sehr schwieriges Problem, dass standardmäßig jedes Form sein eigenes Dataset hat, sodass Daten redundant geladen werden, und die Anwendung sogar in Daten-Konkurrenz mit sich selbst gerät.

    Aber eins nach dem anderen - kannst dann ja nachfragen, wenn du mit den bisherigen 2 Sachen (1 - DateiEigenschaften, 2 - kein Accept-/Reject-Changes) fertig bist.
    Die Datenbank ist ein SQL-Server Express 2014, der auf einem völlig anderen Rechner läuft. "Kopieren wenn neuer" wirds also eher nicht sein - und hätte ich auch sofort selbst zurechtgebogen, hab mir schon mal damit ne blutige Nase geholt ;) Übrigens ist der Master-View ein DataGridView, das nur drei Datenelemente anzeigt, die anderen 15 Elemente liegen daneben - alles in einem Formular. Wird daher eher weniger Probleme geben.

    AcceptChanges und rejectChanges werde ich eliminieren. Dachte, das müsse man aufrufen, meine verfügbare Literatur äußert sich dazu nicht :/
    Etwas OT aber trotzdem: Würde dir gerade wenn du schon nen schönen SQL Server hast, hier Entity Framework als Ersatz zum DataSet empfehlen. DataSets sind im Vergleich einfach hoffnungslos veraltet. Die Möglichkeiten, Performance, etc. ist einfach nicht vergleichbar. Zudem hatte ich da nie Probleme irgendwas zu committen.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    (jo, kanner sich überlegen, ich setze aber erstmal die Dataset-Schiene fort)



    Also ich bin überrascht, dass die Änderung inne Datenbank ankommt, aber beim Neustart dann wieder weg ist, obwohl es keine Datei-basierte DB ist.
    Kannst du das iwie checken, wann die Änderungen aus der DB wieder verschwinden?
    Und es ist mir unerklärlich, wie ein Neustart sowas bewirken kann, denn da muss ja der vorherige Zustand irgendwo anders gespeichert sein, dass er wiederhergestellt wird.
    Tatsächlich glaube ich das einfach nicht.
    Also entweder es ist doch eine Datei-basierte Db, oder die Änderungen landen nicht im Db-Server.
    Oder du hast iwelche ganz erstaunliche Build-Events konfiguriert, die's hinkriegen, beim Testlauf die Datenbank auffm Server wieder zu rekonstruieren.

    Wie gesagt: Ist ausserhalb meines Vorstellungsvermögens - in meiner Welt hat ist das Zurücksetzen der Datenbank nur mit Datenbank-Dateien möglich, die pro Testlauf neu ins AusgabeVerzeichnis kopiert werden.



    Dann scheint es da noch eine 2. Frage zu geben, nämlich, dass Änderungen gemacht wurden, aber im Form_Closing dem Dataset die Änderungen unbekannt sind.

    Ich bin nicht sicher, ob ich die beiden Fragen richtig verstanden habe, und bitte dich um Rückmeldung.
    Und mit welcher Frage möchtest du dich nu zunächstmal auseinandersetzen (weil bei mehreren gleichzeitig geht meist der Überblick verloren)?
    @Olaf,

    ich vermisse deinen SQLComandbuilder, der hätte in diesem Beispiel nicht unbedingt dabei sein müssen, aber das glaub ich liegt die Ursache.
    Der Commandbuilder generiert den Updatestring auf Basis des zuletzt zugewiesenen SQL-Strings, guck noch mal was Du ihm da giebts
    Hallo Freunde,

    sorry das es etwas gedauert hat, war doch etwas hektisch am WE... :whistling: Egal. Ich werde nun keine Zitier-Arie anfangen, dann wirds etwas undurchsichtig.

    Ehrlich gesagt bin ich nicht mal sicher, das überhaupt irgendwas von meinen Eingaben in der SQL-DB landet. Wenn ich das recht verstanden habe, ist das Dataset - egal ob nun typisiert oder nicht - wie eine Art Cache. Durch .Fill wird das Dataset mit Daten gefüllt, die Verbindung zum SQL-Server getrennt und nur noch mit diesen lokalen Daten gearbeitet. Das würde erklären, warum ich in einem Eingabefeld etwas ändern kann und beim Wechsel des Datensatzes im DataGridView meine Änderungen übernommen und angezeigt werden. Das passiert alles nur lokal im Speicher des Dataset.

    Durch .Update sollten nun alle meine Änderungen wieder zurückgeschrieben werden. Einen CommandBuilder brauche ich offenbar nicht, der wird wohl automatisch generiert (alle Tabellen haben Identity-Felder); jedenfalls bekomme ich keinerlei Hinweis oder Fehlermeldung darauf und ganz ehrlich: Wenn meine gesamte Literatur (z.T. von Microsoft selbst !) nicht auf einen zwingend ewrforderlichen CommandBuilder hinweist, ist entweder die Literatur Müll oder das ganze Dataset-Geraffel fürn Arsch :cursing: Ich hatte mich eh schon gefragt, wie das wohl mit meiner Temperatur-Überwachung werden soll, wo sich >16Mio Datensätze in einer Tabelle befinden. Das .Fill dürfte locker eine halbe Stunde dauern... Egal, das ist OT.

    Jedenfalls schreibt das .Update offenbar nix in meine SQL-DB und ich würde gern wissen, wieso - bevor ich das Projekt einreiße und mit Entity Framework wieder aufbaue. Ist zum Glück noch nicht sehr weit fortgeschritten :) Würde etwas Code weiterhelfen (und wenn ja, welcher ?)

    Danke fürs mitgrübeln und -wundern.
    ich warte noch, ob du noch eine klare Frage stellst, weil in diesem Post kann ich keine finden.
    Allenfalls, dass du etwas wissen möchtest, wieso, aber so allgemein kann man nur drauf antworten, dass es an deim Code liegt (den wir ja nicht kennen).

    Eine andere Frage scheint zu sein, ob du EF nehmen sollst, oder Dataset-Technologie, aber da hast du ja bereits 2 diametral entgegengesetzte Antworten - das wird sich auch nicht ändern, wenn du 100mal danach fragst.

    Also kommt noch eine beantwortbare Frage, oder war's das jetzt?
    Etwas freundlicher kann man sowas schon sagen ;)

    Folgender Code funktioniert:

    C#-Quellcode

    1. NeuKunde nc = new NeuKunde();
    2. DialogResult dr=nc.ShowDialog();
    3. if (dr == DialogResult.OK)
    4. {
    5. //Datensatz neu anlegen
    6. ds.Kunden.AddKundenRow(nc.tbKDNR.Text, nc.tbName.Text, PaarAndereFelder.text);
    7. KundenTA.Update(ds);
    8. }


    Mein frisch angelegter Kunde landet zuverlässig in meiner SQL-Datenbank.

    Manuelles abspeichern von Änderugen an einem Kundendatensatz funktioniert aber nicht:

    C#-Quellcode

    1. KundenTA.Update(ds.Kunden);
    2. if (ds.Kunden.HasErrors)
    3. {
    4. //Error anzeigen
    5. }


    Was könnte das Problem sein ?
    sieht richtig aus, aber kann natürlich alles mögliche sein:
    • Der code wird nicht durchlaufen,
    • es wurde ein anderes dataset geändert, als das, was geupdated wird,
    • der TableAdapter failt,
    • ein TryCatch catch iwo Fehler weg,
    • es wurden gar keine Änderungen gemacht
    • Die Eingabe ist noch garnet abgeschlossen - BindingSource.EndEdit steht noch aus
    • jemand hat unsachgemäß AcceptChanges aufgerufen
    • ...

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

    Vollzitat entfernt. ~Trade
    • Wird er. Gesetzter Breakpoint wird erreicht.
    • Es gibt nur ein einziges Dataset. Versprochen.
    • KundenTA.HasErrors zeigt nach durchlaufen der Update-Methode "false" an. Also keine Errors.
    • Try...Catch gibts in diesem 150-Zeiler noch kein einziges
    • Änderungen werden schon gemacht, Versprochen. Das DataGridView zeigt meine Änderung ja auch an.
    • Eingabe wird beendet durch Drücken von TAB, was ein Validate-Ereignis auslöst. DGV zeigt die Änderung an.
    • AcceptChanges und RejectChanges sind allesamt eliminiert
    Ich habe etwas neues herausgefunden. Der Klick auf "Änderungen speichern" scheint nicht zu genügen. Ich muß tatsächlich erst im DatagridView die aktuelle Zeile wechseln. Wird dann mein KundenTA.Update(ds) durchlaufen, landet das ganze auch in der SQL-Datenbank.

    Vielleicht stelle ich die Frage einmal anders: Wie speichere ich programmgesteuert Änderungen in meinem Dataset ab ? Ich möchte einen Button auf der Form haben, und wenn der geklickt wird, gehts in die DB. How to ?

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

    OlafSt schrieb:

    Ich habe etwas neues herausgefunden. Der Klick auf "Änderungen speichern" scheint nicht zu genügen. Ich muß tatsächlich erst im DatagridView die aktuelle Zeile wechseln. Wird dann mein KundenTA.Update(ds) durchlaufen, landet das ganze auch in der SQL-Datenbank.
    You've got it! :thumbsup:
    Dieses Verhaltens bewirkt, dass eine Eingabe komplett gecancelt werden kann.
    Also du kannst 3 Spalten eines Datensatzes eingeben, dann überlegst du dir: "ach lieber doch nicht", cancelst, und nix ist passiert.
    Das ist der EditableObject-Pattern, repräsentiert durch das IEditableObject-Interface, was gute Daten-Klassen implementieren sollten.

    Aber wenn man's nicht kennt, nervts zunächst mal, bis man drauf kommt, dass mit Tab eine Eingabe nicht abgeschlossen ist, sondern erst der Zeilenwechsel, oder auch der Focus-Wechsel auf ein anderes Control (aber nicht Toolstrip oder Menu-Items - die nehmen nämlich keinen Focus an).
    Langer Rede kurzer Sinn: Rufe vorm Abspeichern DatagridView EndCurrentEdit() auf, dann BindingSource.EndEdit(), dann sollte die Übernahme der Eingabe erzwungen sein.
    Alternativ kannst du auch BindingSource.CancelEdit aufrufen, um von gemachten Änderungen zurückzutreten.