![]() |
Datenbank: Firebird • Version: 2.5.2 • Zugriff über: FireDac
Duplicate-Value-Fehler bei Aufruf von Append
Gegeben ist eine Datenbank mit einer Tabelle PERSONEN:
Code:
Nun werden dort Namen automatisiert eingetragen, wobei es gelegentlich vorkommt, daß ein Name bereits eingetragen ist. Da für das Feld PERS_NAME ein eindeutiger Index erstellt wurde, führt der Versuch natürlich zu einer Exception, und zwar beim Post-Befehl.
ID_PERSONEN INTEGER NOT NULL,
PERS_ID INTEGER DEFAULT 0 NOT NULL, PERS_NAME VARCHAR(100) DEFAULT '' NOT NULL COLLATE UNICODE, ALTER TABLE PERSONEN ADD CONSTRAINT PK_PERSONEN PRIMARY KEY (ID_PERSONEN); CREATE UNIQUE INDEX IDX_PERSID ON PERSONEN (PERS_ID); CREATE UNIQUE INDEX IDX_PERSON_NAME ON PERSONEN (PERS_NAME); ... eine Exception der Klasse EIBNativeException mit der Meldung '[FireDAC][Phys][FB]attempt to store duplicate value (visible to active transactions) in unique index "IDX_PERSON_NAME"' aufgetreten. Das ist soweit in Ordnung, weil es abgefangen und in einer Liste dokumentiert wird, wenn ein Name bereits eingetragen ist.
Delphi-Quellcode:
Das ist so gewollt, denn für den Anwender müssen die zu wählenden Namenseinträge später unterscheidbar sein, weil es außer dem Namen keine weiteren Unterscheidungsmerkmale gibt. Die nicht eingetragenen doppelten Namen werden später gesondert behandelt, aber das ist eine andere Geschichte. Nun aber zu meinem Problem:
Function TFrame_PersonenAktuell.Eintragen(Id : Integer; Person : String; Datum : TDateTime; DateExists : Boolean) : Boolean;
begin Try DatMod.Qset_Personen.Append; Feld_Id.AsInteger := Id; Feld_Name.AsString := Person; If DateExists Then Feld_Datum.AsDateTime := Datum; DatMod.Qset_Personen.Post; Result := True; Except On E: Exception Do Begin Result := False; Memo_Log.Lines.Append(e.Message + GLD.TS + IntToStr(Id)); End; End; end; Nachdem die erste Exception korrekt ausgelöst wurde, weil der Name bereits in der Datenbank existiert, folgt der nächste Name, der – von mir extra und aufs genaueste überprüft – tatsächlich noch nicht eingetragen ist. Dennoch löst der Versuch, diesen Namen einzutragen, nun ebenfalls eine Exception aus, und das – jetzt kommt's! – bereits beim Aufruf von Append! Ja, ihr habt richtig gelesen: Bereits beim Aufruf von
Delphi-Quellcode:
erscheint die o.g. Fehlermeldung. Das verstehe ich jetzt erstmal überhaupt gar nicht. Wie kann ein Append einen store duplicate value auslösen? Der DB-Server weiß zu diesem Zeitpunkt doch noch gar nicht, was ihm da reingedrückt werden soll ... also irgendwas stimmt da nicht ...
DatMod.Qset_Personen.Append;
Wenn ich nun im Fehlerdialog auf Anhalten drücke, landet der Prozeß-Cursor in der Unit FireDAC.Stan.Error, und zwar in der Zeile 189:
Delphi-Quellcode:
Darüber steht was Seltsames als Kommentar: The monitor client messages queue may be not empty. When an exception is raised and an IDE is stopped, the queue remains not empty and FDMonitor does not show error message. So, give the CPU to other threads. Könnte es sein, daß ich diese monitor client messages queue (was immer das auch ist) nach einer solchen Exception erst leeren muß, damit das Programm anschließend wieder ordnungsgemäß arbeitet? Weiß jemand, was hier abläuft?
raise oEx;
|
GELÖST: Duplicate-Value-Fehler bei Aufruf von Append
Dieses Problem hat mich offenbar so stark beschäftigt, daß ich ganz unruhig geschlafen habe. Vermutlich hab ich davon geträumt, erinnere mich aber nicht wirklich daran. Auf jeden Fall bin ich vor einer halben Stunde aufgewacht und mir war so gut wie klar, warum das nicht funktionieren konnte, was ich da zusammengestrickt hatte. Die Lösung: Cancel. So einfach und doch so wirkungsvoll:
Delphi-Quellcode:
Da sieht man mal wieder, daß menschliche Gehirne manchmal (oder auch öfter) erst dann auf die Lösung eines Problems stoßen, wenn man sie in Ruhe arbeiten läßt :)
Function TFrame_PersonenAktuell.Eintragen(Id : Integer; Person : String; Datum : TDateTime; DateExists : Boolean) : Boolean;
begin Try DatMod.Qset_Personen.Append; Feld_Id.AsInteger := Id; Feld_Name.AsString := Person; If DateExists Then Feld_Datum.AsDateTime := Datum; DatMod.Qset_Personen.Post; Result := True; Except On E: Exception Do Begin Result := False; // Das ist die Lösung: das Dataset ist noch im Insert-Mode, wenn ich es beim nächsten Mal anspreche If (DatMod.Qset_Personen.State = dsInsert) Or (DatMod.Qset_Personen.State = dsEdit) Then DatMod.Qset_Personen.Cancel; Memo_Log.Lines.Append(e.Message + GLD.TS + IntToStr(Id)); End; End; end; Edit meinte noch: Abgelenkt und auf die falsche Fährte gelockt hatte mich die Fehlermeldung, die ja eigentlich die vom vorherigen Fehler war. Da wäre es doch schön, wenn Delphi melden würde, daß sich das Dateset bereits im Insert-Modus befindet ... doch dann hätten meine grauen Zellen ja nichts zu knobeln gehabt :) |
AW: Duplicate-Value-Fehler bei Aufruf von Append
Insert und Append rufen Post auf, wenn der vorige Satz noch im Editmodus ist.
|
AW: Duplicate-Value-Fehler bei Aufruf von Append
Ahhh – ohhh .. entfleucht es mir gerade lautstark ...
Damit erklärt sich natürlich alles: Das ist nicht die alte Fehlermeldung, da wird beim zweiten Versuch, das Duplikat einzufügen, derselbe Fehler noch einmal ausgelöst. Auf so einen "gemeinen" Fallstrick muß man erst mal kommen :roll: Danke, mkinzler, wieder was dazugelernt :thumb: |
AW: Duplicate-Value-Fehler bei Aufruf von Append
Ich würde die Exceptionbehandlung noch verbessern: Reagiere explizit auf EIBNativeException, anstatt auf Exceptions im allgemeinen.
|
AW: Duplicate-Value-Fehler bei Aufruf von Append
Was ist daran in welcher Hinsicht besser? Immerhin müßte ich, um deinen Vorschlag umzusetzen, eine zusätzliche Unit einbinden. Exception fängt jedoch den Fehler einwandfrei ab, das genügt mir.
|
AW: Duplicate-Value-Fehler bei Aufruf von Append
Eine neue Unit einbinden ist ja jetzt nicht direkt ein Gegenargument, oder? ;-)
Aber warum soll das 'besser' sein? Ganz einfach: Wie ich bereits schrieb, ist es einfach sauberer, explizit auf genau diese Exception zu reagieren, und nicht auf irgend eine. Es kann ja auch nach dem Post geknallt haben, und dann wäre zumindest die Reaktion z.T. eine andere (bzw. könnte sein). Auch wenn Du natürlich argumentieren wirst, das in diesem konkreten Fall gar keine andere Reaktion eintreffen kann oder dir das egal ist, sollte man -auch aus Gründen der Lesbarkeit- genau aufführen, wie man in welchem Ausnahmefall reagiert. Beispielsweise wäre es denkbar, das Du später einmal eine andere Logik hinter das Post packst und spätestens dann solltest Du individuell reagieren. Warum also nicht gleich? |
AW: Duplicate-Value-Fehler bei Aufruf von Append
Zitat:
Delphi-Quellcode:
eben auch für alles Möglich eine Exception bekommen kannst.
try .. except
Wenn du programmtechnisch alles richtig machst, dann wird diese Lösung exakt das machen, was du davon erwartest. Somit also keine Einwände. Die Probleme fangen immer dann an, wenn es aus welchen Gründen auch immer in dem gekennzeichneten Bereich zu einer Exception kommt:
Delphi-Quellcode:
dann reagierst du so, als ob es sich um ein Duplikat handelt.
Try
{problematischer Bereich Anfang} DatMod.Qset_Personen.Append; Feld_Id.AsInteger := Id; Feld_Name.AsString := Person; If DateExists Then Feld_Datum.AsDateTime := Datum; {problematischer Bereich Ende} DatMod.Qset_Personen.Post; Result := True; Except Besser wäre es zumindestens schon mal das so umzustellen
Delphi-Quellcode:
Function TFrame_PersonenAktuell.Eintragen(Id : Integer; Person : String; Datum : TDateTime; DateExists : Boolean) : Boolean;
begin DatMod.Qset_Personen.Append; Feld_Id.AsInteger := Id; Feld_Name.AsString := Person; If DateExists Then Feld_Datum.AsDateTime := Datum; Try // Nur Post-Methode abfangen DatMod.Qset_Personen.Post; Result := True; Except // Eine Einschränkung auf den konkreten Exception-Typ wäre noch besser On E: Exception Do Begin Result := False; // Das ist die Lösung: das Dataset ist noch im Insert-Mode, wenn ich es beim nächsten Mal anspreche If (DatMod.Qset_Personen.State = dsInsert) Or (DatMod.Qset_Personen.State = dsEdit) Then DatMod.Qset_Personen.Cancel; Memo_Log.Lines.Append(e.Message + GLD.TS + IntToStr(Id)); End; End; end; |
AW: Duplicate-Value-Fehler bei Aufruf von Append
Also ich seh das nicht so problematisch, da jede Exception abgefangen und mit der original Message gelogged(!) wird und im Fall eines bestimmten, ggF. problematischen Datasetstate, genau dieser State/ Fall behandelt wird (unabhängig von der exakten Fehlermeldung, aber abhängig eben vom State). Es geschieht hier also nichts falsches.
Ein spezifischeres Exception Handling kann nur in Kombi mit dem allgemeinen geschehen und ist auch nur dann sinnvoll, wenn eine spezifische, dazu passende Codesequence folgt. Vielleicht ist schon das verschieben des Try Blocks auf das Post problematisch, wenn es bei der Wertezuweisung ins Dataset knallt. |
AW: Duplicate-Value-Fehler bei Aufruf von Append
Operationen mit DataSet/Connection laufen doch immer nach dem gleichen Muster ab:
Delphi-Quellcode:
Das kann man fast reflexartig einsetzen und hat damit wenig Probleme, egal ob sich dort ein Programmierfehler oder Eingabefehler reingemogelt hat.
procedure Bar;
begin DataSet.Append; try DataSet.FieldByName('foobar').Value := 'bar from foo'; DataSet.Post; // letzte Aktion im try..except Block except DataSet.Cancel; raise; end; end; procedure Foo; begin Connection.StartTransaction; try Bar; ... (beliebig viele) Bar; Connection.Commit; // letzte Aktion im try..except Block except Connection.Rollback; raise; end; end; Wirft der Aufruf von
Delphi-Quellcode:
keine Exception, dann ist das eben erfolgreich ausgeführt worden.
Foo
Wenn man eine Exception wirklich behandeln kann, dann greift man ein und sorgt dafür, dass die Prozedur ihre geforderte Arbeit erledigen kann oder wirft eine Exception (die gleiche oder eine übersetzte, Hauptsache eine Exception). Das Protokollieren von Exceptions geht auch ganz anders (s. ![]() Ich denke aber wir driften gerade ab ... zum Thema Exception gibt es hier (auch von mir) schon mehr als genug Beiträge ... |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:09 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz