AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

OOP wirklich nicht möglich?

Ein Thema von Delbor · begonnen am 12. Okt 2017 · letzter Beitrag vom 20. Okt 2017
Antwort Antwort
Seite 2 von 2     12   
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.352 Beiträge
 
Delphi 11 Alexandria
 
#1

AW: OOP wirklich nicht möglich?

  Alt 13. Okt 2017, 16:58
Mir geht es wie Elrond.

Ich würde die Logik eher in verschiedene Ebenen aufteilen.

Deine Buinessklassen sollten die Daten so verwalten und die Felder so benennen, wie es für die Geschäftslogik Sinn macht.

Dann müsstest Du Deinen Klassen die Fähigkeit geben, sich in eine Datenbank zu speichern und die eignen Daten daraus wieder zu laden.
Da spielt dann für die Businessklassen keine Rolle, was da für eine Datenbank genutzt wird.

Welche Datenbank genutzt wird und wie die Statements dazu aussehen, könnte in austauschbaren Units oder Komponenten geregelt werden.
Dann kannst Du zunächst MyUnit_MySql verwenden und dort alle bisherigen Funktionen deklarieren und dann diese durch MyUnit_SQLite ersetzen.
Der Interface-Abschnitt müsste die selben Methoden veröffentlichen aber im Implementations-Abschnitt wären diese jeweils für eine andere Datenbank umgesetzt.

Falls Du Dich mit Interfaces auskennst oder Dich damit befassen willst, wären diese eigentlich prädestiniert für solche Anwendungsfälle.

In jedem Fall ist es sinnvoll, die Zuständigkeiten wie Geschäftslogik zum einen und Datenbankverbindung zum anderen möglichst entkoppelt voneinander zu regeln. Für eine detaillierte Umsetzung gibt es dann wieder mehrere Möglichkeiten.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Elrond

Registriert seit: 29. Sep 2014
71 Beiträge
 
#2

AW: OOP wirklich nicht möglich?

  Alt 13. Okt 2017, 18:27
Nun stelle ich mein Programm von MySQL auf SQLite um; gleichzeitig aber habe ich der Datenbank kürzere, aber trotzdem beschreibende Tabellen- und Feldnamen verpasst. Das heisst nichts anderes, als dass ich jetzt eine Klasse bauen darf, die dasselbe wie TQueryresultClass tut, deren Felder, Propertys und Methoden aber anders heissen. So ist zum Beispiel der Name "BilddescribeTabelle" durch "TblBildText" ersetzt worden.
Wenn dies dein Hauptproblem darstellt, dann wäre die Verwendung eines DB Wrappers die sauberste Lösung. Dieser Wrapper generiert dir automatisch aus deiner Datenbank die passenden Delphiklassen. Dieser Ansatz ist etwas ungewöhnlich im vergleich zum klassischen Object-Relational Mapping ala Hibbernate, funktioniert jedoch genauso gut. Somit müsstest du nur deinen Wrapper beim wechseln der Datenbank anpassen.
  Mit Zitat antworten Zitat
Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.192 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: OOP wirklich nicht möglich?

  Alt 14. Okt 2017, 09:39
Hi Elrond

Meinst du sowas?

Gruss
Delbor
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch
  Mit Zitat antworten Zitat
MichaelT

Registriert seit: 14. Sep 2005
Ort: 4020 Linz
558 Beiträge
 
Delphi 10.3 Rio
 
#4

AW: OOP wirklich nicht möglich?

  Alt 18. Okt 2017, 17:39
Mit OOP Mitteln ist interessante Frage.

In einem Satz formuliert. Ist dein Problem, dass du in der TCMQueryClass SQL Statements drinnen hast?

Deine gecashten Objekte schreiben können sich selbst in DB zurückschreiben, etwas platt formuliert ala object.SetDirty und schon rasseln die SQL Statements.

Jetzt hast du eine Applikation die auf beide DBs gleichzeitg zugreift? Soll das so bleiben?

Oder du willst in Zukunft willst du nurmehr auf SQLite gehen oder Wahlweise entweder auf MySQL XOR MySQLite zugreifen?

Falls nur eine Datenbank in Zukunft gefragt ist migriere den Datenbestand.

---

Dein Problem anders definiert heißt der SQL String passt nicht zur DB. In dem Fall bietet sich als billige Variante an die Formatierung des String herauszulösen sofern die Logik für den Zugriff abseits der Basisdatentypen halbwegs indent ist.

Was du brauchst ist eine normiertes SQL. Am besten ist du machst eine 'globale' Variable in der die ZielDB drinnensteht und eine Funktion FormatSQL('SQLStatement....) : String, welche in Abhängigkeit der Ziel DB das richtige SQL zurückgibt.

Die Funktion kann dann die Methode einer Klasse genauso sein und ab dann kannst dahinter rumfuhrwerken wie du willst. Du wirst nicht umhinkommen den Code um zumindest das Klammern der SQL Statements zu erweitern.

SQL.Format(SELECT_VON_TABELLE);

So etwas in die Richtung oder FormatSQL(SELECT_VON_TABELLE) ist an sich pragmatischer.

Konstanten als 'Symbol' für die SQL Statements wirst du leider einführen müssen oder du verwendest String Variablen ... In dem Punkt kann man sich schon ausleben. Wesentlich ist die Behandlung der SQL Statements auf einer 'höheren' Ebene.

Die Prototypen für Formatierung und alles was dazu gehört lagerst du in eine Basisklasse aus. Auf dem Weg geht nicht mehr viel schief. Du kannst die Konstanten in der Klasse definieren usw...

Wenn die Programmlogik sich mit dem Lesen der Blobs massiv ändert müssten wir schauen. Billiger geht es nicht mehr.

Wo du die globale Variable für den Ziel DB Typ ansiedelst kann ich dir jetzt nicht sagen.

Geändert von MichaelT (18. Okt 2017 um 17:57 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.352 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: OOP wirklich nicht möglich?

  Alt 18. Okt 2017, 19:07
@Delbor

Das, was Du beschreibst, klingt schon ganz gut, aber Dein Quelltext lässt das nicht ganz erkennen.

Du könntest es Dir deutlich einfacher machen, aber müsstest dafür sicher Dein Projekt neu aufbauen.
Mal zwei Ansätze.

Empfehlenswert wäre m.E. folgender neuer Ansatz (mal am Beispiel Personen und Autos):

Du baust 2 Klassen:

Delphi-Quellcode:
TPerson = class
  Vornme: string;
  Nachname: string;
  Autos: TList<TAuto>;
  Kilometer: Integer;
end;

TAuto = class
  Typ: string;
  Farbe: string;
  Fahrer: List<TPerson>;
  Kilometer: Integer;
  procedure Fahren(aFahrer: TPerson; aKM: Integer);
end;
Jetzt kannst Du Objekte erzeugen, im Speicher verwalten, in Listen zuordnen und Kilometer für Fahrer und Autos zählen.

Das sind die Businessklassen. Nur das brauchst Du, um die Kilometer für die Fahrer und Autos zu ermitteln.

Jetzt kannst Du schon mal testweise per Code einige Testpersonen und Autos anlegen und einige Strecken fahren.
Also zumindest kannst Du schon mal ein paar Testobjekte erzeugen.

Nun baust Du die GUI, und Du kannst sehen, ob die Testdaten korrekt angezeigt werden und ob Du über die GUI neue erzeugen kannst.
Bis hierher spielt die Datenbank noch keine Rolle!

Aber jetzt wollen wir die Daten natürlich doch irgendwie speichern.
Also müssen die Objekte irgendwie die Fähigkeit haben, gespeichert und gelesen zu werden.
Wenn es dafür eine einheitliche Regelung gibt ist das sicher sinnvoll.
Daher könnte man eine Basisklasse einführen, die das unterstützt:


Delphi-Quellcode:
TSaveLoad = class
  procedure Save; virtual;
  procedure Load; virtual;
end;

TPerson = class(TSaveLoad)
  Vornme: string;
  Nachname: string;
  Autos: TList<TAuto>;
  Kilometer: Integer;
  procedure Save; override;
  procedure Load; override;
end;

TAuto = class(TSaveLoad)
  Typ: string;
  Farbe: string;
  Fahrer: List<TPerson>;
  Kilometer: Integer;
  procedure Fahren(aFahrer: TPerson; aKM: Integer);
  procedure Save; override;
  procedure Load; override;
end;

Wenn man die Daten einfach mal in einer Ini speichern will, kann man das in den überschriebenen Methoden Save und Load realisieren.
Wenn man dann auf eine Datenbank welchselt und später nochmal auf eine andere, ändert man einfach die Methoden wieder ab.


Eine andere Variante wäre, die Basisklasse und die Methoden Save und Load weg zu lassen und statt dessen einen DBManager zu bauen:
Also man erzeugt eine weitere Klasse

Delphi-Quellcode:
TDBManager = class
  function GetAllPersons_WithoutCars: TList<TPerson>;
  function LoadPerson(aPersonId: Integer): TPerson;
  procedure SavePerson(aPerson: TPerson);
end;
Dann weiß nur der DBManager, wie die Verbindung zu den Datenbanktabellen herzustellen ist.
Man kann dann unterschiedliche BDManager für unterschiedliche Datenbanken erstellen und die jederzeit austauschen.



In ALLEN FÄLLEN sollte aber TPerson.Vorname IMMER TPerson.Vorname heißen, selbst wenn der ersten Datenbank die Personentabelle People und das Feld FirstName und in der neuen Datenbank die Tabelle Leute und das Feld Name_1 heisst.
NUR in den Methoden Save und Load bzw. in der anderen Variante in dem DBManager müssen die Anpassungen an die Datenbankänderungen vorgenommen werden.


Um wieder auf Dein Beispiel zurück zu kommen:
Statt z.B. in der Buinessklasse mit FTblBild_IdBild: integer; bzw. FBildTabelleIdBild: integer; zu arbeiten, wäre dort fBildID: integer; sinnvoll, weil die bei der Businesslogik eben interessiert.
Wie die passenden Daten aus einer Ini-Datei, aus Datenbank A, Datenbank B oder von Google geholt werden, ist für die Buinessklasse selbst und für die Benennung der Properties völlig uninteressant.


Genau das ist ja auch ein Ziel der OOP, das man verhalten kapselt und auch überschreiben kann.


Sorry, falls das etwas chaotisch ist. Aber vielleicht hilft das ja dennoch mal auf einen besser strukturierten Ansatz.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)

Geändert von stahli (18. Okt 2017 um 19:11 Uhr)
  Mit Zitat antworten Zitat
Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.192 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 11:11
Hi zusammen

@MichaelT

Zitat:
In einem Satz formuliert. Ist dein Problem, dass du in der TCMQueryClass SQL Statements drinnen hast?
Nein. Beide Klassen können nur die von einem Query gelieferten Ergebnisse jeweils für einen Datensatz über drei 3 Tabellen speichern.

Zitat:
Deine gecashten Objekte schreiben können sich selbst in DB zurückschreiben, etwas platt formuliert ala object.SetDirty und schon rasseln die SQL Statements.
Diese Klassen sind nicht für Insert-Vorgänge vorgesehen, sondern speichern nur die Ergebnisse von Select-Abfragen. Wobei die Bilder erst dann abgefragt werden, wenn sie dargestellt werden sollen.

Zitat:
Jetzt hast du eine Applikation die auf beide DBs gleichzeitg zugreift? Soll das so bleiben?
Oder du willst in Zukunft willst du nurmehr auf SQLite gehen oder Wahlweise entweder auf MySQL XOR MySQLite zugreifen?
Wenn die Geschichte mit SQLite funktioniert, in diesem Fall nicht mehr. Zu einem (viel?) späteren Zeitpunkt kann es interessant sein, wenn die Anwendung auch auf andere DBMSe zugrreifen kann. Das ist aber vorerst nicht gefragt.
Gleichwohl - die MySQL-DB bleibt vorerst bestehen.

Zitat:
Falls nur eine Datenbank in Zukunft gefragt ist migriere den Datenbestand.
Die Datenbank enthält zur Zeit keine Daten. Und bis zur Fertigstellung des Programms wird sie auch nur Testdaten enthalten.

@stahli:
Zitat:
In ALLEN FÄLLEN sollte aber TPerson.Vorname IMMER TPerson.Vorname heißen, selbst wenn der ersten Datenbank die Personentabelle People und das Feld FirstName und in der neuen Datenbank die Tabelle Leute und das Feld Name_1 heisst.
In diesem Beispiel kann auch ich mit meinen katastrophalen Englischkenntnissen nicht gross irren.
Aber sobald Abkürzungen mit im Spiel sind - zum Bleistift 'Tbl_BildText' als (Teil)-String einer Objekt-Unterkasse und 'BildDesribeTabelle' als Tabellenname in der DB wird mE. die Verwechslungsgefahr grösser und somit auch die Fehleranfälligkeit. Das Objekt oder eine Methode desselben sollten also eher möglichst gleichnamige Bezeichner wie die korrespondierenden Tabellen/Felder in der DB haben.

TTblBildText; ist eine Unterklasse von TQueryCMResultclass(Speichert Ergebnisse aus der SQLite-DB).
TBildDescribeTabelle;ist eine Unterklasse von TQueryResultclass(Speichert Ergebnisse aus der MySQL-DB).

Diese beiden Bezeichner werden sich innerhalb ihrer Klasssen nicht ändern.

Gruss
Delbor
Angehängte Grafiken
Dateityp: jpg CM-Datamodell.JPG (171,0 KB, 21x aufgerufen)
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.656 Beiträge
 
Delphi 12 Athens
 
#7

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 11:17
Je mehr ich lese, desto mehr frage ich mich: wozu das Ganze? Soll das ein ORM werden/sein? Dann haben IMO die Tabellen- bzw. Feldnamen in den Objekten nichts zu suchen, die Objekte sollte es nicht interessieren, woher die Daten kommen oder wohin sie gehen, dafür ist eine Schicht zuständig. So wie ich es sehe bringt der ganze Aufwand momentan 0% Nutzen (sofern ich das richtig überblicke, ich kann mich auch irren), sondern sorgt eher für Verwirrung.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.352 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 11:49
Aber sobald Abkürzungen mit im Spiel sind - zum Bleistift 'Tbl_BildText' als (Teil)-String einer Objekt-Unterkasse und 'BildDesribeTabelle' als Tabellenname in der DB wird mE. die Verwechslungsgefahr grösser und somit auch die Fehleranfälligkeit. Das Objekt oder eine Methode desselben sollten also eher möglichst gleichnamige Bezeichner wie die korrespondierenden Tabellen/Felder in der DB haben.
Das sehe ich ganz gegensätzlich.
Die Probleme, die Du hier hast, kommen m.E. nur daher, dass Du Deine Klassenmember entsprechend Deiner Tabellen benennen willst.
Wie die Tabellen ausssehen und wie die heißen, hat aber die Businessklassen letztlich nicht zu interessieren.

Was Du tust, ist ein neues Projekt mit neuen Klassen aufzubauen, weil Du eine andere Datenbank anbinden willst. Das ist definitiv der falsche (aufwändigste) Ansatz.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.192 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 15:14
Hi zusammen

TQueryResultClass arbeitet schon länger zur vollkomenen Zufriedenheit - das Problem, das ich mit dem Threadtitel angesprochen habe, war: Gäbe es eine OOP-Lösung, um TQueryResultClass zu ändern? Änderungsziel: Gleiches Funktionieren, wie ursprünglich implementiert, aber mit andern Bezeichnern. Aber erstmal, was TQueryResultClass tut (und mehr hat diese Klasse auch nicht zu tun):
Delphi-Quellcode:
procedure TFDMySQLDml.SelectBildDaten(FCategoryKey : Integer);
  var SQLString: String; Kath_Id : integer;
begin
  Kath_Id := FCategoryKey;
  SQLString := DefineBildSQL3(Kath_Id); // Liefert den SQL-String zurück
  FDQueryMain.Connection := Self.FDConnectionMySql;
  FDQueryMain.Close;
  FDQueryMain.SQL.Text := SQLString;
  FDQueryMain.Params.CreateParam(ftInteger, 'Kath_Id', ptOutput);
  FDQueryMain.Params[0].AsInteger := Kath_Id;
  FDQueryMain.Open;
  FDQueryMain.First;
  While (Not FDQueryMain.Eof) do
  begin
    FQueryResult := TQueryResultClass.Create(Self);
    FQueryResult.IdBild := (FDQueryMain.FieldByName('BildId').AsInteger);
    [QUOTE]FQueryResult.BildDescribeTabelle.BilddesribeID := FDQueryMain.FieldByName('BildDescribeId').AsInteger;[/QUOTE]
    FQueryResult.BildDescribeTabelle.bildkatID := FDQueryMain.FieldByName('bildkatID').AsInteger;
    FQueryResult.BildDescribeTabelle.BildName := FDQueryMain.FieldByName('BildName').AsString;
    FQueryResult.BildDescribeTabelle.BildBeschreibung := FDQueryMain.FieldByName('Bildbeschreibung').AsString;
    FQueryResult.BildDescribeTabelle.BildLegende := FDQueryMain.FieldByName('Bildlegende').AsString;
    FQueryResult.KategoryTabelle.Kategory := FDQueryMain.FieldByName('Kategorie').AsString;
    FDQueryMain.Next;
    FCategoryBildlist.Add(FQueryResult); // Die Records in TQueryResultClass müssen überprüft werden.
  end;
  FDQueryMain.Close;
end;
Wie ersichtlich ist: Weder das Query noch TQueryResultClass verfügen in dieser Abfrage über einen Stream für Blobdaten. Interesieren tun hier nur gerade mal die Begleitdaten zum Bild.
Das besondere, weswegen es TQueryResultClass überhaupt gibt, sind seine Bilder-Propertis:
Delphi-Quellcode:
    property Thumbnail: TMemoryStream read GetThumbnail write SetThumbnail;
    property Bitmap: TMemoryStream read GetBitmap write SetBitmap;
Die beiden Getter, aam Beispiel von GetThumbnail:
Delphi-Quellcode:
function TQueryResultClass.GetThumbnail: TMemoryStream;
  var AUser, APass : String;
begin
  if not Assigned(FThumbnail) then
  begin
    FThumbnail := TMemorystream.Create;
    Result := FillThumbnail(FThumbnail);
  end else
    Result := FThumbnail;
end;
Die Funktion Fillthumbnail:
Delphi-Quellcode:
function TQueryResultClass.FillThumbnail(var Thumbnail: TMemoryStream):TMemoryStream;
var
  BildID: Integer;
  BlobStream: TStream;
  LNull: string;
  SQLString: string;
begin
    BildID := Self.FidBild;
    SQLString := 'SELECT Bildtabelle.Thumbnail as Thumbnail FROM Bildtabelle WHERE Bildtabelle.idBild = :BildID';
    FDMySQLDml.FDQueryMain.SQL.Text := SQLString;
    FDMySQLDml.FDQueryMain.Params.CreateParam(ftInteger, 'BildID', ptInput);
    FDMySQLDml.FDQueryMain.Params[0].AsInteger := BildID;
    FDMySQLDml.FDQueryMain.Open;
    FDMySQLDml.FDQueryMain.First;
    while not FDMySQLDml.FDQueryMain.Eof do
    begin
      if not FDMySQLDml.FDQueryMain.FieldByName('Thumbnail').IsNull then
      begin
        BlobStream := FDMySQLDml.FDQueryMain.CreateBlobStream(FDMySQLDml.FDQueryMain.FieldByName('Thumbnail'), bmread);
        BlobStream.Position := 0;
        FThumbnail.CopyFrom(BlobStream, Blobstream.Size);
        BlobStream.Free;
        FDMySQLDml.FDQueryMain.Next;
        Result:= FThumbnail;
      end
      else
      begin
        LNull := 'Kein Thumbnail vorhanden';
        FThumbnail.WriteBuffer(LNull, SizeOf(LNull));
      end;
    end;
    FDMySQLDml.FDQueryMain.Close;
end;
Das ist das, was geschieht, wenn zur Darstellung der Datensätze auch auf die Bildpropertys von TQueryResultClass zugegriffen wird.

Und nochmal zur Übereinstimmung von Klassen-/Feld und Tabellen/Feldnamen: Der Klasse tuts nicht weh, wenn ihre Felder gleich heissen, wie diejenigen der Tabelle, und zu Fehlfunktionen kann es auch nicht kommen. Aber was liest sich besser:
Delphi-Quellcode:
FQueryResult.BildDescribeTabelle.bildkatID := FDQueryMain.FieldByName('bildkatID').AsInteger;
// oder
FQueryResult.Id := FDQueryMain.FieldByName('bildkatID').AsInteger;
Von den drei beteiligten Tabellen hat jede eine Id als Primärschlüssel und eine als Fremdschlüssel,und alle sind sie wichtig. Bei übereinstimmenden Namen kannst du in der Zuweisung nicht irren, bei grundsätzlich verschiedenen Namen hingegen schon.

Zitat von DeddyH:
Zitat:
Je mehr ich lese, desto mehr frage ich mich: wozu das Ganze? Soll das ein ORM werden/sein? Dann haben IMO die Tabellen- bzw. Feldnamen in den Objekten nichts zu suchen, die Objekte sollte es nicht interessieren, woher die Daten kommen oder wohin sie gehen, dafür ist eine Schicht zuständig. So wie ich es sehe bringt der ganze Aufwand momentan 0% Nutzen (sofern ich das richtig überblicke, ich kann mich auch irren), sondern sorgt eher für Verwirrung.
Gewissermassen ja. Das ganze, insbesondere der Teil mit dem Nachladen der Bilddaten, ist unter anderem mit deiner tatkräftigen Mithilfe (auf Delphi Treff) entstanden. Dein Kommentar damals: "Ein Mini-Orm".
Ein ORM bildet ja eine komplette Datenbank mit Hilfe von zB. Delphi-Objekten ab. Aber eben eine komplette Datenbank. Meine Klasse gestattet das Nachladen der Bilddaten bei Bedarf und das Iterieren durch die Ergebnismenge bei geschlossenem Query oder sogar geschlossener Verbindung, mehr nicht.


Gruss
Delbor
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:00 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