[Erledigt] Status eines Objekts

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

[Erledigt] Status eines Objekts

Beitrag von MacWomble »

Und hier die nächste Frage:

1. Ich habe eine Klasse, deren Objekt Daten von der DB bezieht. Die Klasse hat auch ein Boolean-Property 'IsChanged'
2. Das Objekt der Klasse wird verwendet, um die Daten in der Form anzuzeigen
3. Beim Beenden der Form werden die Inhalte der Eingabefelder in das Objekt zurückgeschrieben
4. die Daten des Objekts werden in die DB eingetragen

Ich könnte nun anhand von Vergleichen (bei 3.) testen, ob sich der Wert im Objekt tatsächlich verändert hat, weil nur dann ein Speichern (4,) notwendig ist.
Dies artet aber in eine kleinere bis mittlere Orgie aus, weswegen ich mich frage, wie man das besser machen könnte.
Zuletzt geändert von MacWomble am Sa 12. Jan 2019, 12:11, insgesamt 1-mal geändert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Status eines Objekts

Beitrag von theo »

TEdit, TCheckBox, TComboBox etc. haben alle ein OnChange Event.
Das könnte man verwenden um festzuhalten, ob sich was geändert hat.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: Status eines Objekts

Beitrag von MacWomble »

Ja, das ist klar. Jedoch wird dies ja ständig ausgelöst wenn man am tippen ist. Ist es wirklich sinnvoll, dies hier zu verwenden?

Müsste dann jedes mal ausgeführt werden(Bsp): If Not Historie.IsChanged then Historie.IsChanged := True;
Zuletzt geändert von MacWomble am Sa 12. Jan 2019, 11:24, insgesamt 1-mal geändert.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Status eines Objekts

Beitrag von theo »

MacWomble hat geschrieben:Ja, das ist klar. Jedoch wird dies ja ständig ausgelöst wenn man am tippen ist. Ist es wirklich sinnvoll, dies hier zu verwenden?

Müsste dann jedes mal ausgeführt werden: If Not Historie.IsChanged then Historie.IsChanged := True;


Wo ist das Problem?
Beim jedem Tippen in ein Edit etc. passiert so viel im System, dass diese eine Boolean Abfrage nicht bemerkbar ist.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: Status eines Objekts

Beitrag von MacWomble »

OK, das klingt plausibel.

Was ist dann besser?

If Not Historie.IsChanged then Historie.IsChanged := True;

oder einfach

Historie.IsChanged := True;
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: Status eines Objekts

Beitrag von pluto »

Ich mache das so: das ich erst speichere, wenn ich die Komponente verlasse z.b. im onExit. Das Klappt sehr Zuverlässig.
Außerdem habe ich noch eine 3 Sekunden Regel: Wenn nach 3 Sekunden keine weiteren Daten geändert wurden wird es gespeichert.
Beim Tippen wird natürlich der Timer zurück gesetzt.
MFG
Michael Springwald

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: Status eines Objekts

Beitrag von theo »

MacWomble hat geschrieben:OK, das klingt plausibel.

Was ist dann besser?

If Not Historie.IsChanged then Historie.IsChanged := True;

oder einfach

Historie.IsChanged := True;


"Sauber" wäre schon If not... aber der Unterschied wird minimal sein.
Insbesondere weil eine Tastatureingabe aus Sicht des Computers sowieso ein seltenes Ereignis ist.
Ich würde mir darüber keine Gedanken machen.

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: Status eines Objekts

Beitrag von MacWomble »

pluto hat geschrieben:Ich mache das so: das ich erst speichere, wenn ich die Komponente verlasse z.b. im onExit. Das Klappt sehr Zuverlässig.
Außerdem habe ich noch eine 3 Sekunden Regel: Wenn nach 3 Sekunden keine weiteren Daten geändert wurden wird es gespeichert.
Beim Tippen wird natürlich der Timer zurück gesetzt.


Die 3-Sekunden-Regel finde ich witzig, ist aber nicht unbedenklich:

Stell dir vor, der Anwender schläft ein, schlägt mit dem Kopf auf die Tastatur und dann wird hgjsjödhgehhö gespeichert :shock: :P :oops:

Besser ist ein Speichern-Button und das automatische Speichern beim Verlassen der Form.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Benutzeravatar
theo
Beiträge: 10467
Registriert: Mo 11. Sep 2006, 19:01

Re: [Erledigt] Status eines Objekts

Beitrag von theo »

OnExit feuert aber auch, wenn nichts verändert wurde.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Status eines Objekts

Beitrag von fliegermichl »

Ich mache das normalerweise in den Setter Methoden der Properties der Klasse.

Code: Alles auswählen

 
interface
type
 
 TMyClassProperty = (mcpModifed, mcpVisible, ...);
 TMyClassProperties = set of TMyClassProperty;
 
 TMyClass = class
 private
  fFlags : TMyClassProperties;  // Hier lassen sich jede Menge boolscher Flags unterbringen
  fName : string;
  fWhatEver : Double;
  procedure SetName(const aValue : string);
  procedure SetWhatEver(const aValue : Double);
  procedure SetModified(const aValue : boolean);
  function  GetModified : boolean;
 public
  property Name : string read fName write SetName;
  property WhatEver : integer read fWhatEver write SetWhatEver;
  property Modified : boolean read GetModified write SetModified;
 end;
 
implementation
 
procedure TMyClass.SetName(const aValue : string);
begin
 if fName = aValue then exit; // Den neuen Wert nur dann verwenden wenn er ungleich dem bisherigen ist
 fName := aValue;
 Modified := True;
end;
 
procedure TMyClass.SetWhatEver(const aValue : integer);
begin
 if fWhatEver = aValue then exit;
 fWhatEver := aValue;
 Modified := True;
end;
 
procedure TMyClass.SetModified(const aValue : boolean);
begin
 if Modified = aValue then exit; // Hier muss man aufpassen, dass auf die Property Modified nicht schreibend zugegriffen wird. Sonst dreht sich es endlos im Kreis!
 if aValue then
  Include(fFlags, mcpModified)
 else
  Exclude(fFlags, mcpModified);
end;
 
function TMyClass.GetModified : boolean;
begin
 Result := mcpModified in fFlags;
end;
 


Das hat den Vorteil, dass die Property Modified wirklich nur dann true liefert, wenn eine der anderen Properties mit einem vom bisherigen Inhalt abweichenden Wert beschrieben wurde.
Gerade wenn man mit Datenbanken arbeitet, kann man sich u.U. ein Update sparen wenn z.B. der alte Name "Schmidt" ist und der Anwender in einem Edit was macht aber am Ende doch wieder "Schmidt" drinnensteht.

oft verwende ich hirarchische Strukturen die von TFPList abgeleitet sind.
In dem Fall prüft GetModified nicht nur den Status von sich selbst sondern auch den aller untergeordneter Elemente und liefert nur dann true wenn sich irgendein Element geändert hat.

Ein weiterer Vorteil besteht darin, dass in den Settermethoden (SetName, SetWhatEver usw) noch eine Plausibilitätsprüfung der neuen Inhalte gemacht werden kann.

Auf den ersten Blick sieht das nach viel Schreibarbeit aus. Es hilft einem bei größeren Projekten aber auch etwas den Überblick zu behalten. (Mein CAD Programm hat mittlerweile fast 600000 Programmzeilen und ich blicke noch immer durch), meistens jedenfalls :-)

Gruß
Michael

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [Erledigt] Status eines Objekts

Beitrag von MacWomble »

Diese Lösung finde ich sehr gut. Alles ist in der Klasse geregelt, unabhängig von der Eingabemaske.
Das spart die vielen OnChange-Ereignisse und macht das ganze dadurch auch noch sicherer (Vergessenes OnChange = kein Speichern).
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: [Erledigt] Status eines Objekts

Beitrag von pluto »

OnExit feuert aber auch, wenn nichts verändert wurde.

Das ist richtig, dort kann man aber prüfen, ob was verändert wurde oder nicht.... wo ist das Problem?

Stell dir vor, der Anwender schläft ein, schlägt mit dem Kopf auf die Tastatur und dann wird hgjsjödhgehhö gespeichert

Das macht im Prinzip nichts, da es eine Histroytable gibt.
dank dieser tabelle kann man jede Änderung wider rückgängig machen.

Zum Beispiel bei Google-Docs wird ebenfalls Automatisch gespeichert ob nach 3 Sekunden weiß ich nicht, aber so bin ich auf die Idee gekommen.

edit: Ich finde es viel schlimmer daten zu verlieren, weil ich kurz weg gehe und dann der Strom ausfällt oder irgendwas anders nicht ok ist z.b. so ein Windws Update meint, den PC neu zu starten(kann unter linux auch mal vorkommen....)

Besser ist ein Speichern-Button und das automatische Speichern beim Verlassen der Form.

Altmodisch....(Aus meiner Sicht).

Ich mache das normalerweise in den Setter Methoden der Properties der Klasse.

Zur Zeit mache ich es ebenfalls so... Weil sich nur das Übernommen werden muss was auch geändert wurde.
Z.B. wenn ich ein From habe mit 12 Eingabefeldern, sich aber nur 1 Feld Geändert hat, warum gleich alles speichern?
MFG
Michael Springwald

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [Erledigt] Status eines Objekts

Beitrag von MacWomble »

Einen Speichern-Button benötige ich auch nicht, da die Form zum Editieren / Eingeben eines einzelnen Datensatzes aufgerufen und nach der Eingabe direkt wieder geschlossen wird. Über eine automatische (zeitliche) Sicherung nachzudenken ist sicher sinnvoll. Aber ich konnte mir den Wink nicht verkneifen. :lol:

so ein Windws Update meint, den PC neu zu starten(kann unter linux auch mal vorkommen....)
 

Kam bei mir unter Linux noch nie vor, kann ich mir auch nicht vorstellen - aber von Windoof kenne ich das noch :twisted:

Z.B. wenn ich ein From habe mit 12 Eingabefeldern, sich aber nur 1 Feld Geändert hat, warum gleich alles speichern?

... und wie viele Schreibzugriffe hast du, wenn 12 Felder geändert werden, bzw wie gehst du dann vor?

Ich schreibe bzw aktualisiere den kompletten Datensatz, wenn irgend ein Feld geändert wurde.

Ich verfolge im Moment zwei Ziele in meinem Projekt:
1. Die Datenmaske soll die Datenbank (oder was auch immer) nicht kennen
2. Die Lese- und Schreibzugriffe sollen auf ein absolutes Minimum reduziert werden.

Ersteres realisiere ich durch die Einführung von Datenklassen, letzteres durch den Tipp von Fliegermichl

Ich habe hierfür ein kleines Testprojekt und alles funktioniert nun wie gewünscht - Bisher, aber ich habe noch einiges vor mir :o
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

pluto
Lazarusforum e. V.
Beiträge: 7178
Registriert: So 19. Nov 2006, 12:06
OS, Lazarus, FPC: Linux Mint 19.3
CPU-Target: AMD
Wohnort: Oldenburg(Oldenburg)

Re: [Erledigt] Status eines Objekts

Beitrag von pluto »

... und wie viele Schreibzugriffe hast du, wenn 12 Felder geändert werden, bzw wie gehst du dann vor?

Meine DB Funktion ist extra dafür ausgelegt. es gibt eine Klasse z.b. für alle 12 Felder und wenn sich dort was ändert wird eine Variable auf true gestellt....
Daraus mache ich dann eine Datenbank abfrage.... Die Funktion ist recht lang und noch nicht aufgeräumt.....

Code: Alles auswählen

 
procedure TPLNoteManager2.EditNote(const aNote: TPLNoteManagerNoteItem);
var
  str:string;
 
  UsedNoteID, UsedDirectoryID, UsedTitle, UsedText:Boolean;
  UsedCreateDateTime, UsedLastReadDateTime, UsedLastWriteDatetime:Boolean;
  UsedReadCount, UsedWriteCount, UsedEncryptByUserPassword:Boolean;
 
  FirstFeld:Boolean;
begin
  str := 'UPDATE NoteTable SET ';
  UsedNoteID:=false; UsedDirectoryID:=false; UsedTitle:=false; UsedText:=false;
  UsedCreateDateTime:=false; UsedLastReadDateTime:=false; UsedLastWriteDatetime:=false;
  UsedReadCount:=false; UsedWriteCount:=false; UsedEncryptByUserPassword:=false;
  FirstFeld:=False;
 
{  if aNote.NoteID > 0 then begin
    FirstFeld:=true;
    str:=str+'NoteID=:NoteID';
    UsedNoteID:=true;
  end; // NoteID}

 
  if aNote.DirectoryID > 0 then begin
    if FirstFeld then
      str:=str+','
    else
      FirstFeld:=true;
 
    str:=str+'DirectoryID=:DirectoryID';
    UsedDirectoryID:=true;
  end; // DirectoryID
 
  if aNote.Title <> '' then begin
    writeln('Title');
    if FirstFeld then
      str:=str+','
    else
      FirstFeld:=true;
    str:=str+'Title=:Title';
    UsedTitle:=true;
  end; // Title
 
  if aNote.Text <> '' then begin
    writeln('Text');
    if FirstFeld then
      str:=str+','
    else
      FirstFeld:=true;
    str:=str+'Text=:Text';
    UsedText:=true;
  end; // Text
 
  if aNote.CreateDateTime > 0 then begin
    if FirstFeld then
      str:=str+','
    else
      FirstFeld:=true;
    str:=str+'CreateDateTime=:CreateDateTime';
    UsedCreateDateTime:=true;
  end; // CreateDateTime
 
  if aNote.LastReadDateTime > 0 then begin
    if FirstFeld then
      str:=str+','
    else
      FirstFeld:=true;
    str:=str+'LastReadDateTime=:LastReadDateTime';
    UsedLastReadDateTime:=true;
  end; // LastReadDateTime
 
  if aNote.LastWriteDateTime > 0 then begin
    if FirstFeld then
      str:=str+','
    else
      FirstFeld:=true;
    str:=str+'LastwriteDateTime=:LastwriteDateTime';
    UsedLastWriteDatetime:=true;
  end; // LastWriteDateTime
 
  if aNote.ReadCount > 0 then begin
    if FirstFeld then
      str:=str+','
    else
      FirstFeld:=true;
    str:=str+'ReadCount=:ReadCount';
    UsedReadCount:=true;
  end; // ReadCount
 
  if aNote.WriteCount > 0 then begin
    if FirstFeld then
      str:=str+','
    else
      FirstFeld:=true;
    str:=str+'WriteCount=:WriteCount';
    UsedWriteCount:=true;
  end; // WriteCount
 
  str:=str+' WHERE NoteID = ' + IntToStr(aNote.NoteID)+';';
  writeln(#13,str);
 
  MariaDB.Query.SQL.Text:=str;
  if UsedNoteID then MariaDB.Query.ParamByName('NoteID').AsInteger:=aNote.NoteID;
  if UsedDirectoryID then MariaDB.Query.ParamByName('DirectoryID').AsInteger:=aNote.DirectoryID;
 
  if UsedTitle then MariaDB.Query.ParamByName('Title').AsString:=aNote.Title;
  if UsedText then MariaDB.Query.ParamByName('Text').AsString:=aNote.Text;
 
  if UsedCreateDateTime then MariaDB.Query.ParamByName('CreateDateTime').AsDateTime:=aNote.CreateDateTime;
  if UsedLastReadDateTime then MariaDB.Query.ParamByName('LastReadDateTime').AsDateTime:=aNote.LastReadDateTime;
  if UsedLastWriteDatetime then MariaDB.Query.ParamByName('LastwriteDateTime').AsDateTime:=aNote.LastWriteDateTime;
 
  if UsedReadCount then MariaDB.Query.ParamByName('ReadCount').AsInteger:=aNote.ReadCount;
  if UsedWriteCount then MariaDB.Query.ParamByName('WriteCount').AsInteger:=aNote.WriteCount;
 
  MariaDB.Query.ExecSQL;
  MariaDB.ATransaction.Commit;
  writeln('TPLNoteManager2.EditNote');
end; // TPLNoteManager2.EditNote   
 

Ich weiß nicht, ob man das optimieren kann, aber geht bestimmt...
Diese Procedure funktioniert sehr gut.
Der Code kommt aus meinem Aktuellen Prototyp.

Ich schreibe bzw aktualisiere den kompletten Datensatz, wenn irgend ein Feld geändert wurde.

Es mag Fälle geben, da geht es nicht anders... mein Ansatz war es Datentraffic zu sparen. Die Genannte Procedur haben, ist teil einer Rest Api die ich gerade schreibe.

1. Die Datenmaske soll die Datenbank (oder was auch immer) nicht kennen

Richtig, ist bei mir auch der Fall.... Hinzu kommt, das zwischen Rest-server und Rest-Client ein Jsonen-Austausch Protokoll verwendet wird...

2. Die Lese- und Schreibzugriffe sollen auf ein absolutes Minimum reduziert werden.

Eben... darum nur das speichern, was sich auch geändert hat.
MFG
Michael Springwald

MacWomble
Lazarusforum e. V.
Beiträge: 999
Registriert: Do 17. Apr 2008, 01:59
OS, Lazarus, FPC: Mint 21.1 Cinnamon / FPC 3.2.2/Lazarus 2.2.4
CPU-Target: Intel i7-10750 64Bit
Wohnort: Freiburg

Re: [Erledigt] Status eines Objekts

Beitrag von MacWomble »

pluto hat geschrieben:
.
2. Die Lese- und Schreibzugriffe sollen auf ein absolutes Minimum reduziert werden.

Eben... darum nur das speichern, was sich auch geändert hat.

Gut, aber in der Regel sind die Datensätze nicht groß und meines Erachtens ist es unerheblich ob ich nun den kompletten Satz aktualisiere oder nur die geänderten Felder.
In jedem Fall habe ich ein Update zu schreiben. Bei Insert ist es unter Umständen sogar kritisch, zumindest wenn diese keinen Default in der DB definiert haben.
Natürlich hast du Recht, wenn du vom Trafficvolumen ausgehst.

Du kennst ja mein CTR-BOSS vom sehen und ich kann dir sagen, dass es im Moment unzählige überflüssige Lese- und Schreibzugriffe macht, was den festen Datenbindungen an die Formcontrols zu schulden ist.
Mit den Klassen bin ich da nun wirklich auf das notwendige Minimum unten - weniger geht tatsächlich nur wenn man es so minimiert, wie du es tust.
Alle sagten, dass es unmöglich sei - bis einer kam und es einfach gemacht hat.

Antworten