![]() |
Delphi-Version: XE5
Set of Enumeration Type in Datenbank-Parameter zuweisen
Hallo zusammen,
ich habe eine vermutlich ganz triviale Frage. Der Laufzeitfehler bezüglich Typkonvertierung beschäftigt mich allerdings schon eine Weile, ohne dass ich bisher eine Lösung gefunden hätte.
Delphi-Quellcode:
Den Wert bestimme ich per RTTI und bekomme einen Wert vom Typ TValue zurück. Jetzt möchte ich diesen in der Datenbank als Integer speichern. Dabei kommt aber immer ein Laufzeitfehler "Ungültige Typumwandlung", obwohl ich in value.AsInteger den korrekten Wert im Debugger sehen kann. Das Feld Flags ist in der Datenbank als Integer angelegt.
type
TBankFlag = (fbfEinzahlung, fbfAuszahlung); type TBankFlags = set of TBankFlag; // Diese in im Objekt als Property vorhanden type TFoo = type(TObject) FFlags: TBankFlags; published Flags: TBankFlags read FFlags write FFlags; end; foo.Flags := [fbfEinzahlung, fbfAuszahlung] -> value.AsInteger = 3 foo.Flags := [fbfEinzahlung] -> value.AsInteger = 1
Delphi-Quellcode:
Egal welchen Typ ich mir von TValue mit .AsXXX geben lasse, es knallt beim Zuweisen dieses Parameters. Noch eine Anmerkung, Flags kann je nach Objekt verschiedene ENum-Bedeutungen haben. Also ist AsType<T> für mich nicht so zielführend.
..
var value: TValue; begin // ...value-Bestimmung... qry.ParamByName('FLAGS').AsInteger := value.AsInteger; end; Geht das überhaupt, was ich da vorhabe? |
AW: Set of Enumeration Type in Datenbank-Parameter zuweisen
Früher hat man das so gemacht:
Delphi-Quellcode:
qry.ParamByName('FLAGS').AsInteger := Byte(value);
Ein Set ist nunmal kein "ordinaler" Typ. Oder du gehst über die RTTI und wandelst das in einen String um. |
AW: Set of Enumeration Type in Datenbank-Parameter zuweisen
Hallo,
Hab grad rumprobiert und hatte auch so meine Probleme, aber so gehts:
Delphi-Quellcode:
var b: Byte;
begin // ... value.ExtractRawData(@b); qry.ParamByName('FLAGS').AsInteger := b; // ODER qry.ParamByName('FLAGS').AsInteger := PByte(value.GetReferenceToRawData)^; end; |
AW: Set of Enumeration Type in Datenbank-Parameter zuweisen
Ich würde es als String in die Datenbank speichern. Wenn da später jemand draufguckt, könnte man noch eine ungefähre Vorstellung dafür bekommen, was das ist.
Abgesehen davon- Kannst du deinem Enum nicht eine Helfer-Methode verpassen?
Delphi-Quellcode:
type
TMeinEnum = (Hund, Katze, Maus); TMeinEnumHelper = record helper for TMeinEnum function ToString(): String; function ToInteger(): Integer; // function ToField(): TField // oder so ähnlich? end; implementation uses System.TypInfo; function TMeinEnumHelper.ToString(): String; begin Result := GetEnumName( TypeInfo(TMeinEnum), // Den Namen hier nochmal manuell zu nennen muss wohl... Ord(self) ); end; function TMeinEnumHelper.ToInteger(): Integer; begin Result := Ord(self); end; |
AW: Set of Enumeration Type in Datenbank-Parameter zuweisen
Danke für die superschnelle Hilfe, ich werde für den Moment mal mit Michaels Ansatz weiterarbeiten.
Das mit der Helper-Klasse hätte ich wohl schon gemacht, wenn im rechten Teil des Helpers auch ein generischer Typ angegeben werden könnte :) Ich habe bisher drei unterschiedliche Typen von "FLAGS", je nachdem in welchem Objekt. Und jedem Set sein eigenes Helper-Objekt an die Seite zu stellen mit dreimal dem gleichen Quellcode bis auf den Typ :( Die Überlegung das Ganze in Strings in die Datenbank zu speichern ist gut, erschwert aber nachher die Suche. Mit dem Integer brauche ich nur mit logischem AND eine Bitposition in FLAGS abfragen. So war zumindest meine Grundüberlegung. Aber wenn ich so drüber nachdenke, könnte ich das ganze Set auch in eigenes Objekt verschieben und mit einzelnen boolschen Property-Werten arbeiten. Macht den Quellcode lesbarer, aber die nachfolgende Speicherung und Suche auch nicht gerade übersichtlicher. |
AW: Set of Enumeration Type in Datenbank-Parameter zuweisen
Eine Alternative und von den Restriktionen des ENUM-SET gänzlich unabhängige Variante wäre die, die einzelnen Ausprägungen des Enums in eine Tabelle zu packen und eine n:m-Beziehung zwischen dem Objekt und der Enum-Tabelle zu erstellen.
Das ist eine allgemeingültige Lösung, die vollständig im DB-Schema dokumentiert ist und die es erlaubt, sehr einfach Queries zu erstellen, ohne im Code zu schauen, wie den das SET nun kodiert wurde. Einfach ausgedrückt: Tabelle 'Enum'
|
AW: Set of Enumeration Type in Datenbank-Parameter zuweisen
Zitat:
Delphi-Quellcode:
So läßt sich damit arbeiten:
uses
TypInfo; type TEnumHelper = class private FEnumInfo: PTypeInfo; public Constructor Create(AEnumInfo: PTypeInfo); function ToString(const EnumValue): string; //typloser Parameter function ToEnum(const Name: string): integer; end; implementation constructor TEnumHelper.Create(AEnumInfo: PTypeInfo); begin FEnumInfo := AEnumInfo; end; function TEnumHelper.ToString(const EnumValue): string; begin Result := GetEnumName(FEnumInfo, ord(TOrdType(EnumValue))); //typloser Parameter wird gecastet end; function TEnumHelper.ToEnum(const Name: string): integer; begin Result := GetEnumValue(FEnumInfo, Name); end;
Delphi-Quellcode:
Auf eine Lösung, wie ich diesen letzten Cast einsparen kann, bin ich noch nicht gekommen. Wenn nur eine Zahl herauskommen soll, passt es.
//Enumeration:
TTasteBuds = (tbSour, tbSweet, tbSalty, tbSpicey, tbUmami) //Erzeugung und Verwendung TasteBuds := TEnumHelper.Create(TypeInfo(TTasteBuds)); showMessage(TasteBuds.ToString(tbSour)); //"tbSour" //oder andere Richtung var SomeTasteBud: TTasteBuds; SomeTasteBud := TTasteBuds(TasteBuds.ToEnum(tbSour)); //hier muß zurückgecastet werden. :( |
AW: Set of Enumeration Type in Datenbank-Parameter zuweisen
Nja, es geht auch andersrum.
Delphi-Quellcode:
Byte(SomeTasteBud) := TasteBuds.ToEnum(tbSour);
oder
Delphi-Quellcode:
var
SomeTasteBudByte: Byte absolute SomeTasteBud; SomeTasteBud := TasteBuds.ToEnum(tbSour); Oder du definierst das TEnumHelper.ToEnum als Prozedur und gibst das Result ebenfalls als typlosen VAR/OUT-Parameter raus, genauso wie beim CONST. Wenn du dir in den letzten paar 5 Jahren mal ein neueres Delph zugelegt hättest, dann ginge auch sowas.
Delphi-Quellcode:
Das entspricht in etwa deinem Code, wenn man den Typ direkt mit übergibt
RTTIServices = class
class function SetToStr<TSet>(const Value: TSet; Brackets: Boolean=True): string; static; class function StrToSet<TSet>(const Value: string): TSet; static; class function EnumToStr<TEnum>(const Value: TEnum): string; static; class function StrToEnum<TEnum>(const Value: string): TEnum; static; end; var S: string; E: TTasteBuds; S := RTTIServices.EnumToStr<TTasteBuds>(tbSour); E := RTTIServices.StrToEnum<TTasteBuds>('tbSour');
Delphi-Quellcode:
ShowMessage(TasteBuds.ToString(TypeInfo(TTasteBuds), Ord(tbSour))); // 'tbSour'
SomeTasteBud := TTasteBuds(TasteBuds.ToEnum(TypeInfo(TTasteBuds), 'tbSour')); // tbSour Es liese sich im Aufruf nochmal etwas kürzen, wenn man den generischen Typ nicht an die Methoden, sondern an die Klasse bindet.
Delphi-Quellcode:
type
EnumService<TEnum> = class class function EnumToStr(const Value: TEnum): string; static; class function StrToEnum(const Value: string): TEnum; static; end; type X = EnumService<TTasteBuds>; var S: string; E: TTasteBuds; S := EnumService<TTasteBuds>.EnumToStr(tbSour); E := EnumService<TTasteBuds>.StrToEnum('tbSour'); S := X.EnumToStr(tbSour); E := X.StrToEnum('tbSour'); Und nein, TOrdType ist nicht immer richtig. Eigentlich msstest du aus dem TypeInfo die Enum-Größe auslesen und dann entweder nach Byte, Word oder Integer casten, denn wenn dein ENUM mehr als 256 Werte enthält, dann hast du ein Problem. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:55 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 by Thomas Breitkreuz