![]() |
[RTTI] RttiProperty aus Property von Instanz erzeugen
Hallo,
hänge gerade mal wieder an einem neuen Problem mit Rtti und ich weiß nicht, wie ich das machen sollte. Ich habe mehrere Properties, die mit Werten aus der Datenbank befüllt werden. Die Setter-Methoden dieser Properties sollen auf eine generische Methode verweisen, die das Setzen der Werte für alle relevanten Properties übernehmen kann. Wichtig hierbei ist die Prüfung auf das Attribut NotNull, d.h. beim Setzen eine Wertes wird geprüft, ob ein Null-Wert gesetzt werden darf oder nicht. Hier ein vereinfachtes Szenario, sodass das verständlicher wird:
Delphi-Quellcode:
So, nun ist es ja so, dass, wie im Quelltext schon geschrieben, GetRttiProperty eine fiktive Funktion ist, die ich suche. Ich will eben aus der Property PropA bzw. PropB ein TRttiProperty-Objekt machen, ohne dabei auf einen String zurückgreifen zu müssen. Sollte sich nämlich mal etwas an den Properties ändern, so kann ich nicht einfach das Refactoring anwerfen, sondern darf selbst nach Strings suchen, was alles andere als sauber wäre.
TBlub = class(TObject)
private FNotNullProperties : TStringList; // wird im Konstruktor mit FPropA : Variant; FPropB : Variant; procedure GenericSetter(const AValue: Variant; const ADestVar: PVariant; const AProperty: TRttiProperty); procedure SetPropA(const AValue: Variant); procedure SetPropB(const AValue: Variant); public [NotNull] property PropA : Variant read FPropA write SetPropA; property PropB : Variant read FPropB write SetPropB; end; procedure TBlub.GenericSetter(const AValue: Variant; const ADestVar: PVariant; const AProperty: TRttiProperty); var rContext : TRttiContext; rType : TRttiType; begin if (AValue <> ADestVar^) then begin if (AValue <> Null) then begin ADestVar^ := AValue; end else begin // Prüfen, ob Property überhaupt Null werden darf, denn wenn nicht dann muss eine Exception // geworfen werden if (FNotNullProperties.IndexOf(AProperty.Name) <> -1) then raise Exception.Create('...'); ADestVar^ := Null; end; end; end; procedure TBlub.SetPropA(const AValue: Variant); begin GenericSetter(AValue, FPropA, GetRttiProperty(PropA)); // GetRttiPropertyist eine fiktive Funktion end; procedure TBlub.SetPropB(const AValue: Variant); begin GenericSetter(AValue, FPropB, GetRttiProperty(PropB)); // GetRttiPropertyist eine fiktive Funktion end; Ich hoffe, dass mir hier jemand weiter helfen kann. Bin gerade echt am verzweifeln :roll: |
Re: [RTTI] RttiProperty aus Property von Instanz erzeugen
So funktioniert aber kein mir bekannter O/R-Mapper (wenn es auch nur annähernd in diese Richtung gehen soll). Die Datenobjekte sollten reine PODOs (Plain Old
![]() |
Re: [RTTI] RttiProperty aus Property von Instanz erzeugen
Vielleicht hilft dir
![]() |
Re: [RTTI] RttiProperty aus Property von Instanz erzeugen
Zitat:
Man sieht an deinem Beispiel schon, welche Nachteile du hast. Man muss bei neuen Properties speziellen Code schreiben und kann nicht einfach eine simple neue Property in die Klasse einbauen. Zu deiner eigentlichen Frage: ich kenne keine Möglichkeit über eine Property-"Referenz" an die RTTI Information dieser zu kommen. |
Re: [RTTI] RttiProperty aus Property von Instanz erzeugen
Jo, es soll im Endeffekt ein O/R-Mapping statt finden. Ich fand das Attribut-Prinzip ganz hilfreich für genau so etwas. Ich habe auch für jede Property ein Attribut, welchem ich einen String übergebe. Dieser entspricht einer Spalte aus der Datenbank. Somit baue ich im Moment meine Abbildung zusammen, d.h. die Properties können einen anderen Namen tragen als die Spalten aus der DB. Ich habe mir schon überlegt, das in einer XML zu definieren oder über ein Array zu lösen, aber da habe ich *immer* das Problem, dass kein Refactoring sinnvoll greift, wenn ich denn einmal eine Property umbenennen will. Daher hielt ich das für die beste Lösung.
Und genau das wollte ich nun eigentlich für genau das oben beschriebene Problem auch habe. Änderungen sollen einfach via Refactoring möglich sein, ohne, dass ich nicht x-beliebig viele andere Stellen anspringen muss und Strings ändern sollte. Nur ich seh schon, ich muss das irgendwie anders gestalten. Abseits davon: Wie machen das "normale" O/R-Mapper denn? Es wird ja ein Abbildungs-Verzeichnis benötigt. Wo wird das gespeichert? Wie werden zusätzliche Dinge beachtet? Not Null z.B.? Wird das alles fix in einen Controller implementiert? |
Re: [RTTI] RttiProperty aus Property von Instanz erzeugen
Gegen Attribute haben wir doch gar nichts eingewendet :D . Es ging lediglich um den Setter-Code.
Im einfachsten Fall wird für alle Public Properties jeweils eine gleichnamige DB-Spalte gesucht. Durch Attribute können sie zusätzlich umbenannt oder ignoriert werden, auch Validation-Informationen wie NotNull kannst du damit durchaus hinzufügen. |
Re: [RTTI] RttiProperty aus Property von Instanz erzeugen
Zitat:
|
Re: [RTTI] RttiProperty aus Property von Instanz erzeugen
Ah, ich habe mich auf das Einfügen der DB-Daten in die Entities konzentriert, eben der erste und wahrscheinlich leichteste Schritt auf dem Weg zu einem O/RM :) . Für solche Sachen wie Change Tracking und Validation muss tatsächlich etwas im Setter geschehen, da hast du schon recht. Wenn man also ein Validate('ThisProperty') vermeiden will, sehe ich bei statisch typisierten Sprachen nur zwei Möglichkeiten:
|
Re: [RTTI] RttiProperty aus Property von Instanz erzeugen
Irgendwie ist das aber immer noch nicht so ganz das was ich mir darunter vorgestellt habe. Selbst wenn ich intern die Daten nicht wie eine Property handle (z.B. in einem passenden indexierten Container), führt das früher oder später zu ungewollten Problemen, da ich dann wieder keine Attribute für die einzelnen Werte anwenden kann. Ich stoße dabei immer wieder an Ecken, die nicht so toll sind.
Es wäre echt wünschenswert aus einer Property eine TRttiProperty-Referenz zu erhalten. Mal schauen, was das neue Delphi so bringen wird. |
Re: [RTTI] RttiProperty aus Property von Instanz erzeugen
Ich habe nun eine Lösung, die über die Setter-Methode einer Property läuft. Diese Idee funktioniert allerdings nur unter der Voraussetzung, dass jede Property eine eindeutige und einzigartige Setter-Methode hat, denn wenn für mehrere Properties ein und die selbe Setter-Methode verwendet wird (was ja unter Umständen der Fall sein kann), so wäre dies keine eindeutige Abbildung und so käme auch keine korrekt Lösung heraus.
Delphi-Quellcode:
Vielleicht hilft das ja mal jemandem. Sinnvoll ist diese Methode im Moment nur, wenn diese intern (private) verwendet wird, oder man setzt die Setter-Methoden public, sodass man darauf auch von außen zugreifen kann.
// Prozeduraler Typ für Parameter
TMyProc = procedure (const Value: Variant) of object; // Test-Klasse ;) TTestObject = class(TObject) private FPropA : String; FPropB : String; function GetPropA(): String; procedure SetPropA(const Value: String); function GetPropB(): String; procedure SetPropB(const Value: String); public property PropA : Stringread GetPropA write SetPropA; property PropB : Stringread GetPropB write SetPropB; function GetRttiPropertyBySetter(const AMethod: TMyProc): TRttiProperty; end; implementation { TTestObject } function TTestObject.GetPropA(): String; begin Result := FPropA; end; procedure TTestObject.SetPropA(const Value: String); var pt : Pointer; begin { ... } end; function TTestObject.GetPropB(): String; begin Result := FPropA; end; procedure TTestObject.SetPropB(const Value: String); begin { ... } end; function TTestObject.GetRttiPropertyBySetter(const AMethod: TMyProc): TRttiProperty; var pt : Pointer; rContext : TRttiContext; rType : TRttiType; rProperty : TRttiProperty; rPropInfo : PPropInfo; begin Result := nil; // Pointer auf Setter-Methode holen pt := @AMethod; rContext := TRttiContext.Create(); try rType := rContext.GetType(Self.ClassType); // Property für Property durchgehen, bis eine gefunden wurde // die eine passende Setter-Methode (Erkennung via Pointer) hat. for rProperty in rType.GetDeclaredProperties do begin rPropInfo := TRttiInstanceProperty(rProperty).PropInfo; if (rPropInfo.SetProc = pt) then begin Result := rProperty; exit; end; end; finally rContext.Free(); end; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:37 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