|
Registriert seit: 3. Sep 2004 441 Beiträge Delphi 10.4 Sydney |
#5
Schon mal danke für die Rückmeldungen!
![]() Es ist immer sinnvoll, wenn alle Programme, die auf eine Datenbank zugreifen, den gleichen und vollständigen Connectionstring nutzen und nicht einen, der mal so gerade eben (hoffentlich) den Minimalanforderungen gerecht wird.
Delphi-Quellcode:
Beim Betrachten der TAdoConnection in diesem neuen Form ist mir wieder in den Sinn gekommen, dass ja viele Attribute direkt am Delphi-Objekt geändert werden können (z.b. db.Mode := cmShareDenyNone; ). Ob dies jetzt Vorrang vor dem 'Mode=Share Deny None;' aus dem ConnectionString hat, sich evtl. sogar wiederspricht oder ignoriert wird: Ich habe es vorsichtshalber nochmal explizit reingeschrieben, im notfall gilt hier: "Doppelt hält besser", da die Werte ja an beiden Stellen identisch sind.
db.Close;
db.Mode := cmShareDenyNone; db.KeepConnection := HAL_Registry_GetBool('KeepConnection', True); db.LoginPrompt := False; db.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;' + 'User ID=Admin;' + 'Data Source=' + _FileName + ';' + 'Mode=Share Deny None;' + 'Jet OLEDB:System database="";' + 'Jet OLEDB:Registry Path="";' + 'Jet OLEDB:Database Password="";' + 'Jet OLEDB:Engine Type=5;' + 'Jet OLEDB:Database Locking Mode=1;' + 'Jet OLEDB:Global Partial Bulk Ops=2;' + 'Jet OLEDB:Global Bulk Transactions=1;' + 'Jet OLEDB:New Database Password="";' + 'Jet OLEDB:Create System Database=False;' + 'Jet OLEDB:Encrypt Database=False;' + 'Jet OLEDB:Don''t Copy Locale on Compact=False;' + 'Jet OLEDB:Compact Without Replica Repair=False;' + 'Jet OLEDB:SFP=False;' + 'Persist Security Info=False;'; // if db.CursorLocation <> clUseServer then begin // db.CursorLocation := clUseServer; // end; c1 := db.ConnectionString; db.Open; c2 := db.ConnectionString; Die Idee mit dem "Minimalismus" war und ist auch die Angst vor Exceptions beim Verbindungsaufbau. Falls mein Programm jetzt unter einer anderen Windows-Version eingesetzt wird, und ich ein Attribut mitgebe, was dort vielleicht nicht bekannt ist oder unterstützt wird: Jetzt geht hier gar nichts mehr (Verbindungsaufbau aller Datenbanken schlägt fehl), weil ein -von mir eh nicht "gewolltes"- übergebens Attribut nicht bekannt ist. Wahrscheinlich passiert gar nichts, und die Sorge ist unbegründet. Z.B. verwende ich seit Jahren 'Persist Security Info=False;' - der Assistent baut das Attribut auch beim Verbindungsaufbau nicht mit ein - zu stören scheint es aber auch nicht. Was mir hier noch Sorgen macht, ist db.CursorLocation. Ich habe in der OnlineHilfe und im Internet schon einiges drüber gelesen, weiß aber nicht, "ob" und in "wie weit" das für mich relevant ist. Nehmen wir mal an, alle Abfragen bei allen Tabellen und Kunden liefern i.d.R. nicht mehr als 20 Datensätze zurück. Hat CursorLocation jetzt eine Auswirkung auf irgendwelche Datensatz- oder Tabellensperren? Auf die Geschwindigkeit? In der OH steht u.a. "Viele Server unterstützen außerdem nur Vorwärts-Cursor, bei denen der Satzzeiger in der Ergebnismenge nicht in Richtung Tabellenanfang bewegt werden kann." --> Unerwartetes Verhalten / Probleme beim Ausführen einfacher Operationen wie AdoQuery.First etc.? Hört sich gefährlich an... Generell würde ich den Wert ja "unverändert" auf clUseClient lassen. -Spricht was dagegen? -TAdoQuery hat auch die Eigenschaft "CursorLocation". Hier ebenfalls clUseClient? ![]() Ich würde mal schauen, ob ich auf Seite des Dateiservers, der die Access Datei und die Lockdatei hostet, einen genauen Überblick über die (offenen) Dateihandles bekommen kann
-Erzeugt nur jede neue TAdoConnection.Open ein zusätzliches Handle auf die .mdb-Datei oder auch schon z.B. jedes TAdoQuery mit .Open? Bei der großen Umstellung (Link siehe Oben) wurde auch flächig der Zugriff der AdoQuerys überarbeitet. In der Tat habe ich schon bei vielen Kunden auf den FileServern gesehen, das es *schäm* sehr, sehr viele offene Handles auf die Datenbankdatei gegeben hat. Aus anderen Beiträgen hier im Forum habe ich entnommen, dass es nicht langt, ein AdoQuery einfach nur freizugeben (mit FreeAndNil), nein es MUSS explizit mit "Close" geschlossen werden. Bestätigung, Ja, Nein, andere Meinung? Beispielhaft sieht mein Zugriff auf die Datenbank immer so oder ähnlich aus:
Delphi-Quellcode:
-try..finally wäre natürlich innerhalb von try..except feiner, und so würde ich das heute auch machen. Aber so muss q auch ordentlich freigegeben werden: q wird ja am Anfang mit NIL initialisiert, und im schlimmsten Fall wird FreeAndNil(Nil) aufgerufen - es passiert nichts schlimmes. Im Falle einer Exception wird nun das wirklich erzeute q ordnungsgemäß freigegeben, da das Programm nach except..end weiterläuft, und diese Zeile somit in jedem Fall aufgerufen wird. Daran sollte es -wenn auch "ungewöhnlich" im vergleich zu einem eingebetteten try..finally- also nicht liegen, dass unnötige FileHandles auf dem Server offen bleiben.
function TRecurrencePattern.SaveToDatabase(db: TAdoQuery): Boolean;
var i: integer; q: TADOQuery; begin Result := False; q := NIL; try if not assigned(db) then begin Log('TRecurrencePattern.SaveToDatabase: db unassigned.'); Exit; end; q := TADOQuery.Create(Self); q.Connection := db; CloseQueryAndClearSQLText(q); // diese vier Zeilen sind in eigenen Prozeduren mit dortiger Fehlerbehandlung, nur zum Verständnis hier direkt q.SQL.Add('SELECT * FROM T_SerienMuster WHERE refTermin=:ID'); q.Parameters.ParamByName('ID').Value := _refTermin; q.Open; q.First; if q.Eof then q.Insert else q.Edit; q.FieldByName('refTermin').AsInteger := TerminID; q.FieldByName('blRecurrsJAN').AsBoolean := blRecurrsJanuar; q.FieldByName('blRecurrsFEB').AsBoolean := blRecurrsFebruar; q.FieldByName('blRecurrsMAR').AsBoolean := blRecurrsMaerz; q.FieldByName('blRecurrsAPR').AsBoolean := blRecurrsApril; q.FieldByName('blRecurrsMAY').AsBoolean := blRecurrsMai; // usw. q.Post; Result := True; except on E: Exception do begin Log('TRecurrencePattern.SaveToDatabase', M, E.Message); end; end; FreeAndNil(q); -Diese Prozedur ist wieder ein Paradebeispiel für (meine) schlampige Arbeit: nach dem q.Post fehlt das q.Close. Meine frühere Erwartung an TAdoQuery waren schlicht und ergreifend: Wenn ich es freigebe, wird es schon selbst für eine ordentliche Beendigung sorgen (.Close), und nicht einfach "das Messer in der Sau stecken lassen". Wenn ich das richtig verstehe, ist/könnte aber genau das sein, und ich muss dem TAdoQuery noch explizit hinterherräumen. Wenn man's weiß ist ja gut - wenn man mit einer falschen Erwartung dran geht... naja - woher soll man's wissen. Auch hier spielt wieder die "Angst" vor einer Exception eine große Rolle. Wenn ich das Programm jetzt wie folgt um schreiben würde:
Delphi-Quellcode:
Für FreeAndNil sollte es zunächst keinen Unterschied machen. Wenn es aber nun -aus welchen Gründen auch immer- vorkommen *könnte* dass die Erzeugung von q fehlschlägt, bzw. dieses im Laufe der Prozedur auf einen ungültigen Arbeitsspeicherbereich zeigt (jaja, Paranoia etc., aber spielen wir das Spiel mal zu Ende, bitte!) bzw. eine Exception zwischendrin ausgelöst wird, und finally nun angesteuert wird: q.Close würde/könnte hier eine *zusätzliche* Exception schmeißen (weil q ja ungültig oder der Close-Befehl "einfach so" eine Exception wirft), und dann wird FreeAndNil(q) ja gar nicht aufgerufen --> wirkliches MemoryLeak.
function TRecurrencePattern.SaveToDatabase(db: TAdoQuery): Boolean;
var i: integer; q: TADOQuery; begin Result := False; q := NIL; try try if not assigned(db) then begin Log('TRecurrencePattern.SaveToDatabase: db unassigned.'); Exit; end; q := TADOQuery.Create(Self); q.Connection := db; CloseQueryAndClearSQLText(q); // diese vier Zeilen sind in eigenen Prozeduren mit dortiger Fehlerbehandlung, nur zum Verständnis hier direkt q.SQL.Add('SELECT * FROM T_SerienMuster WHERE refTermin=:ID'); q.Parameters.ParamByName('ID').Value := _refTermin; q.Open; q.First; if q.Eof then q.Insert else q.Edit; q.FieldByName('refTermin').AsInteger := TerminID; q.FieldByName('blRecurrsJAN').AsBoolean := blRecurrsJanuar; q.FieldByName('blRecurrsFEB').AsBoolean := blRecurrsFebruar; q.FieldByName('blRecurrsMAR').AsBoolean := blRecurrsMaerz; q.FieldByName('blRecurrsAPR').AsBoolean := blRecurrsApril; q.FieldByName('blRecurrsMAY').AsBoolean := blRecurrsMai; // usw. q.Post; Result := True; finally q.Close; FreeAndNil(q); end; except on E: Exception do begin Log('TRecurrencePattern.SaveToDatabase', M, E.Message); end; end; -Wie setzt man sowas korrekt um? Wie habt Ihr das gelöst? -Ist
Delphi-Quellcode:
genau richtig, falsch, übertrieben, ...?
finally
try q.close; finally FreeAndNil(q); end; end; Danke im vorraus!
Delphi 10.4 32-Bit auf Windows 10 Pro 64-Bit, ehem. Delphi 2010 32-Bit auf Windows 10 Pro 64-Bit
|
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |