Added foReadonly and foWriteOnly os MVCActiveRecord Field Options

This commit is contained in:
Daniele Teti 2020-08-11 00:54:42 +02:00
parent 2e42f10466
commit a690bea612
15 changed files with 291 additions and 44 deletions

View File

@ -404,6 +404,55 @@ Congratulations to Daniele Teti and all the staff for the excellent work!" -- Ma
- `IMVCJSONRPCExecutor.ExecuteNotification` returns a `IJSONRPCResponse`. In case of error response contains information about the error, in case of successful execution the response is a [Null Object](https://en.wikipedia.org/wiki/Null_object_pattern).
- Added `foReadOnly` and `foWriteOnly` as field options in `MVCTableField` attribute (used by `TMVCActiveRecord`). Currently available field options are:
- *foPrimaryKey* { it's the primary key of the mapped table }
- *foAutoGenerated* { not written, read - similar to foReadOnly but is reloaded after insert and update }
- *foTransient* { never stored nor read - managed only at run-time }
- *foReadOnly* { not written, read }
- *foWriteOnly* { written, not read }
Now it is possible to declare entities like the followings (or with any other combinations):
```delphi
[MVCNameCase(ncLowerCase)]
[MVCTable('articles')]
TArticleWithWriteOnlyFields = class(TCustomEntity)
private
[MVCTableField('ID', [foPrimaryKey, foAutoGenerated])]
fID: NullableInt32;
[MVCTableField('description', [foWriteOnly])]
fDescription: string;
[MVCTableField('price', [foWriteOnly])]
fPrice: Integer;
public
property ID: NullableInt32 read fID write fID;
property Description: string read fDescription write fDescription;
property Price: Integer read fPrice write fPrice;
end;
[MVCNameCase(ncLowerCase)]
[MVCTable('articles')]
TArticleWithReadOnlyFields = class(TCustomEntity)
private
[MVCTableField('ID', [foPrimaryKey, foReadOnly])]
fID: NullableInt32;
[MVCTableField('code', [foTransient])]
fCode: NullableString;
[MVCTableField('description', [foReadOnly])]
fDescrizione: string;
[MVCTableField('price', [foReadOnly])]
fPrice: Currency;
public
property ID: NullableInt32 read fID write fID;
property Code: NullableString read fCode write fCode;
property Description: string read fDescription write fDescription;
property Price: Currency read fPrice write fPrice;
end;
```
## Roadmap
DelphiMVCFramework roadmap is always updated as-soon-as the features planned are implemented. Check the roadmap [here](roadmap.md).

View File

@ -46,9 +46,9 @@ type
[MVCTable('articles')]
TArticle = class(TCustomEntity)
private
[MVCTableField('ID')]
[MVCTableField('ID', [foPrimaryKey, foAutoGenerated])]
fID: NullableInt32;
[MVCTableField('code')]
[MVCTableField('code', [foTransient])]
fCodice: NullableString;
[MVCTableField('description')]
fDescrizione: string;
@ -63,6 +63,41 @@ type
property Price: Currency read fPrezzo write fPrezzo;
end;
[MVCNameCase(ncLowerCase)]
[MVCTable('articles')]
TArticleWithWriteOnlyFields = class(TCustomEntity)
private
[MVCTableField('ID', [foPrimaryKey, foAutoGenerated, foReadOnly])]
fID: NullableInt32;
[MVCTableField('description', [foWriteOnly])]
fDescrizione: string;
[MVCTableField('price', [foWriteOnly])]
fPrice: Integer;
public
property ID: NullableInt32 read fID write fID;
property Description: string read fDescrizione write fDescrizione;
property Price: Integer read fPrice write fPrice;
end;
[MVCNameCase(ncLowerCase)]
[MVCTable('articles')]
TArticleWithReadOnlyFields = class(TCustomEntity)
private
[MVCTableField('ID', [foPrimaryKey, foReadOnly])]
fID: NullableInt32;
[MVCTableField('code', [foTransient])]
fCodice: NullableString;
[MVCTableField('description', [foReadOnly])]
fDescrizione: string;
[MVCTableField('price', [foReadOnly])]
fPrezzo: Currency;
public
property ID: NullableInt32 read fID write fID;
property Code: NullableString read fCodice write fCodice;
property Description: string read fDescrizione write fDescrizione;
property Price: Currency read fPrezzo write fPrezzo;
end;
TOrder = class;
[MVCNameCase(ncLowerCase)]
@ -108,7 +143,6 @@ type
fID: Integer;
[MVCTableField('code', [foTransient])]
fCode: string;
[MVCTableField('', [foTransient])]
fFormattedCode: string;
[MVCTableField('description')]
fCompanyName: string;
@ -459,7 +493,7 @@ end;
constructor TNullablesTest.Create;
begin
inherited Create;
// ff_blob := TMemoryStream.Create;
ff_blob := TMemoryStream.Create;
end;
destructor TNullablesTest.Destroy;

View File

@ -158,6 +158,15 @@ object MainForm: TMainForm
TabOrder = 13
OnClick = btnCountWithRQLClick
end
object btnReadAndWriteOnly: TButton
Left = 8
Top = 523
Width = 121
Height = 33
Caption = 'R/O, R/W'
TabOrder = 14
OnClick = btnReadAndWriteOnlyClick
end
object FDConnection1: TFDConnection
Left = 176
Top = 56

View File

@ -42,6 +42,7 @@ type
btnCRUDWithStringPKs: TButton;
btnWithSpaces: TButton;
btnCountWithRQL: TButton;
btnReadAndWriteOnly: TButton;
procedure btnCRUDClick(Sender: TObject);
procedure btnInheritanceClick(Sender: TObject);
procedure btnMultiThreadingClick(Sender: TObject);
@ -58,6 +59,7 @@ type
procedure btnCRUDWithStringPKsClick(Sender: TObject);
procedure btnWithSpacesClick(Sender: TObject);
procedure btnCountWithRQLClick(Sender: TObject);
procedure btnReadAndWriteOnlyClick(Sender: TObject);
private
procedure Log(const Value: string);
procedure LoadCustomers;
@ -137,7 +139,7 @@ begin
Log('There are ' + TMVCActiveRecord.Count<TCustomer>().ToString + ' row/s for entity ' + TCustomer.ClassName);
lCustomer := TCustomer.Create;
try
Log('Entity ' + TCustomer.ClassName + ' is mapped to table ' + lCustomer.TableName);
Log('Entity ' + TCustomer.ClassName + ' is mapped to table ' + lCustomer.TableName);
lCustomer.CompanyName := 'Google Inc.';
lCustomer.City := 'Montain View, CA';
lCustomer.Note := 'Hello there!';
@ -449,7 +451,12 @@ begin
lTest.f_int2 := 2;
lTest.f_int4 := 4;
lTest.f_int8 := 8;
lTest.f_blob := TStringStream.Create('Hello World');
with TStreamWriter.Create(lTest.f_blob) do
try
write('Hello World');
finally
Free;
end;
lTest.Insert;
Log('Inserting nulls');
finally
@ -474,8 +481,7 @@ begin
lTest.f_int2 := lTest.f_int2.Value + 2;
lTest.f_int4 := lTest.f_int4.Value + 4;
lTest.f_int8 := lTest.f_int8.Value + 8;
lTest.f_blob.Free;
lTest.f_blob := nil;
lTest.f_blob.Size := 0;
lTest.Update;
finally
lTest.Free;
@ -494,7 +500,7 @@ begin
Assert(not lTest.f_float4.HasValue);
Assert(not lTest.f_float8.HasValue);
Assert(not lTest.f_bool.HasValue);
Assert(not Assigned(lTest.f_blob), 'Blob contains a value when should not');
Assert(lTest.f_blob.Size = 0, 'Blob contains a value when should not');
TMVCActiveRecord.DeleteRQL(TNullablesTest, 'eq(f_int2,4)');
finally
lTest.Free;
@ -572,6 +578,65 @@ begin
end;
procedure TMainForm.btnReadAndWriteOnlyClick(Sender: TObject);
var
lArtWO, lArtWO2: TArticleWithWriteOnlyFields;
lArtRO: TArticleWithReadOnlyFields;
lID: NullableInt32;
lArt: TArticle;
begin
lArtWO := TArticleWithWriteOnlyFields.Create();
try
lArtWO.Description := 'Description1';
lArtWO.Price := 12;
lArtWO.Insert;
Log('Stored TArticleWithWriteOnlyFields');
lID := lArtWO.ID;
lArt := TMVCActiveRecord.GetByPK<TArticle>(lID);
try
Assert(lArtWO.Description = lArt.Description);
Assert(lArtWO.Price = lArt.Price);
Log('Check Stored version of TArticleWithWriteOnlyFields');
Log('Reading data using TArticleWithReadOnlyFields');
lArtRO := TMVCActiveRecord.GetByPK<TArticleWithReadOnlyFields>(lID);
try
Assert(lArtRO.Description = lArt.Description);
Assert(lArtRO.Price = lArt.Price);
Log('Check Read data of TArticleWithWriteOnlyFields using TArticleWithReadOnlyFields');
finally
lArtRO.Free;
end;
Log('Reading data using TArticleWithWriteOnlyFields (???)');
lArtWO2 := TMVCActiveRecord.GetByPK<TArticleWithWriteOnlyFields>(lID);
try
Assert(lArtWO2.ID.ValueOrDefault = lID.ValueOrDefault);
Assert(lArtWO2.Description = '');
Assert(lArtWO2.Price = 0);
finally
lArtWO2.Free;
end;
finally
lArt.Free;
end;
lArtRO := TArticleWithReadOnlyFields.Create();
try
lArtRO.Description := 'Description1';
lArtRO.Price := 12;
ShowMessage('Now an exception will be raised...');
lArtRO.Insert; // exception here :-)
finally
lArtRO.Free;
end;
finally
lArtWO.Free;
end;
end;
procedure TMainForm.btnRelationsClick(Sender: TObject);
var
lCustomer: TCustomerEx;
@ -818,7 +883,7 @@ begin
lCustomer := TCustomerWithTransient.Create;
try
{
'Code' and City will not be persisted because defined as 'transient'
'Code' will not be persisted because defined as 'transient'
}
lCustomer.Code := '1234';
lCustomer.CompanyName := 'Google Inc.';

View File

@ -163,13 +163,13 @@
</Excluded_Packages>
</Delphi.Personality>
<Deployment Version="3">
<DeployFile LocalName="bin\activerecord_showcase.exe" Configuration="BUILD" Class="ProjectOutput">
<DeployFile LocalName="bin\activerecord_showcase.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>activerecord_showcase.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="bin\activerecord_showcase.exe" Configuration="Debug" Class="ProjectOutput">
<DeployFile LocalName="bin\activerecord_showcase.exe" Configuration="BUILD" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>activerecord_showcase.exe</RemoteName>
<Overwrite>true</Overwrite>

Binary file not shown.

View File

@ -58,7 +58,12 @@ type
end;
TMVCActiveRecordClass = class of TMVCActiveRecord;
TMVCActiveRecordFieldOption = (foPrimaryKey, foAutoGenerated, foTransient);
TMVCActiveRecordFieldOption = (
foPrimaryKey, { it's the primary key of the mapped table }
foAutoGenerated, { not written, read - similar to readonly }
foTransient, { not written, not read }
foReadOnly, { not written, read }
foWriteOnly); { written, not read }
TMVCActiveRecordFieldOptions = set of TMVCActiveRecordFieldOption;
TMVCEntityAction = (eaCreate, eaRetrieve, eaUpdate, eaDelete);
TMVCEntityActions = set of TMVCEntityAction;
@ -81,7 +86,7 @@ type
TFieldInfo = class
public
// TableName: string;
// TableName: string;
FieldName: string;
FieldOptions: TMVCActiveRecordFieldOptions;
DataTypeName: string;
@ -91,11 +96,13 @@ type
TFieldsMap = class(TObjectDictionary<TRTTIField, TFieldInfo>)
private
fNonTransientFieldsCount: Integer;
fWritableFieldsCount: Integer;
fReadableFieldsCount: Integer;
public
constructor Create;
procedure EndUpdates;
property NonTransientFieldsCount: Integer read fNonTransientFieldsCount;
property WritableFieldsCount: Integer read fWritableFieldsCount;
property ReadableFieldsCount: Integer read fWritableFieldsCount;
function GetInfoByFieldName(const FieldName: string): TFieldInfo;
end;
@ -155,7 +162,7 @@ type
function GetPrimaryKeyIsAutogenerated: Boolean;
procedure SetPrimaryKeyIsAutogenerated(const Value: Boolean);
function GetPrimaryKeyFieldType: TFieldType;
procedure SetTableName(const Value: String);
procedure SetTableName(const Value: string);
protected
fRTTIType: TRttiInstanceType;
fProps: TArray<TRTTIField>;
@ -282,7 +289,7 @@ type
procedure AddChildren(const ChildObject: TObject);
procedure RemoveChildren(const ChildObject: TObject);
[MVCDoNotSerialize]
property TableName: String read fTableName write SetTableName;
property TableName: string read fTableName write SetTableName;
[MVCDoNotSerialize]
property PrimaryKeyIsAutogenerated: Boolean read GetPrimaryKeyIsAutogenerated write SetPrimaryKeyIsAutogenerated;
class function GetByPK(const aClass: TMVCActiveRecordClass; const aValue: int64;
@ -452,7 +459,7 @@ type
// end-capabilities
function CreateSQLWhereByRQL(const RQL: string; const Mapping: TMVCFieldsMapping;
const UseArtificialLimit: Boolean = True;
const UseFilterOnly: Boolean = False
const UseFilterOnly: Boolean = false
): string; virtual; abstract;
function CreateSelectSQL(const TableName: string; const Map: TFieldsMap;
const PKFieldName: string; const PKOptions: TMVCActiveRecordFieldOptions): string; virtual; abstract;
@ -603,6 +610,13 @@ begin
lName := aName.ToLower;
lConnKeyName := GetKeyName(lName);
{ If the transaction is not started, initialize TxIsolation as ReadCommitted }
if aConnection.Transaction = nil then
begin
{ needed for Delphi 10.4 Sydney+ }
aConnection.TxOptions.Isolation := TFDTxIsolation.xiReadCommitted;
end;
fMREW.BeginWrite;
try
lConnHolder := TConnHolder.Create;
@ -955,7 +969,7 @@ begin
begin
fPrimaryKeyFieldType := ftLargeInt;
end
else if lPrimaryFieldTypeAsStr.EndsWith('integer') then
else if lPrimaryFieldTypeAsStr.EndsWith('integer') or lPrimaryFieldTypeAsStr.EndsWith('int32') then
begin
fPrimaryKeyFieldType := ftInteger;
end
@ -975,9 +989,8 @@ begin
continue;
end;
{ TODO -oDanieleT -cGeneral : Definire TFieldInfo per tute le info del field }
lFieldInfo := TFieldInfo.Create;
//lFieldInfo.TableName := fTableName;
// lFieldInfo.TableName := fTableName;
lFieldInfo.FieldName := MVCTableFieldAttribute(lAttribute).FieldName;
lFieldInfo.FieldOptions := MVCTableFieldAttribute(lAttribute).FieldOptions;
lFieldInfo.DataTypeName := MVCTableFieldAttribute(lAttribute).DataTypeName;
@ -1010,10 +1023,11 @@ begin
OnValidation(TMVCEntityAction.eaCreate);
OnBeforeInsert;
OnBeforeInsertOrUpdate;
if fMap.NonTransientFieldsCount = 0 then
if fMap.WritableFieldsCount = 0 then
begin
raise EMVCActiveRecord.CreateFmt
('Cannot insert an entity if all fields are transient. Class [%s] mapped on table [%s]', [ClassName, fTableName]);
('Cannot insert an entity if all fields are not writable or transient. Class [%s] mapped on table [%s]',
[ClassName, fTableName]);
end;
if (foAutoGenerated in fPrimaryKeyOptions) then
begin
@ -1049,7 +1063,7 @@ begin
lSQL := Self.SQLGenerator.CreateSelectCount(fTableName);
if not RQL.IsEmpty then
begin
lSQL := lSQL + fSQLGenerator.CreateSQLWhereByRQL(RQL, GetMapping, False, True);
lSQL := lSQL + fSQLGenerator.CreateSQLWhereByRQL(RQL, GetMapping, false, True);
end;
Result := GetScalar(lSQL, []);
end;
@ -1135,19 +1149,24 @@ class function TMVCActiveRecordHelper.GetByPK<T>(const aValue: int64;
const RaiseExceptionIfNotFound: Boolean = True): T;
var
lActiveRecord: TMVCActiveRecord;
lLoaded: Boolean;
begin
Result := T.Create;
lActiveRecord := TMVCActiveRecord(Result);
if not lActiveRecord.LoadByPK(aValue) then
try
lLoaded := lActiveRecord.LoadByPK(aValue);
except
FreeAndNil(Result);
raise;
end;
if not lLoaded then
begin
Result.Free;
FreeAndNil(Result);
if RaiseExceptionIfNotFound then
begin
raise EMVCActiveRecordNotFound.Create('Data not found');
end
else
begin
Result := nil;
end;
end;
end;
@ -2247,7 +2266,7 @@ begin
end;
end;
procedure TMVCActiveRecord.SetTableName(const Value: String);
procedure TMVCActiveRecord.SetTableName(const Value: string);
begin
fTableName := Value;
end;
@ -2361,7 +2380,7 @@ begin
OnValidation(TMVCEntityAction.eaUpdate);
OnBeforeUpdate;
OnBeforeInsertOrUpdate;
if fMap.NonTransientFieldsCount = 0 then
if fMap.WritableFieldsCount = 0 then
begin
raise EMVCActiveRecord.CreateFmt
('Cannot update an entity if all fields are transient. Class [%s] mapped on table [%s]', [ClassName, fTableName]);
@ -2378,7 +2397,7 @@ begin
begin
fChildren := TObjectList<TObject>.Create(True);
end;
if not(fChildren.Contains(ChildObject)) and (not (ChildObject = Self)) then
if not(fChildren.Contains(ChildObject)) and (not(ChildObject = Self)) then
begin
fChildren.Add(ChildObject);
end;
@ -2659,7 +2678,14 @@ begin
Result := Copy(Result, 1, Length(Result) - Length(Delimiter));
if not PKFieldName.IsEmpty then
begin
Result := GetFieldNameForSQL(PKFieldName) + ', ' + Result;
if not Result.IsEmpty then
begin
Result := GetFieldNameForSQL(PKFieldName) + ', ' + Result
end
else
begin
Result := GetFieldNameForSQL(PKFieldName)
end;
end;
end;
@ -2744,20 +2770,27 @@ end;
constructor TFieldsMap.Create;
begin
inherited Create([doOwnsValues]);
fNonTransientFieldsCount := 0;
fWritableFieldsCount := -1;
fReadableFieldsCount := -1;
end;
procedure TFieldsMap.EndUpdates;
var
lPair: TPair<TRTTIField, TFieldInfo>;
begin
fNonTransientFieldsCount := 0;
fWritableFieldsCount := 0;
fReadableFieldsCount := 0;
for lPair in Self do
begin
lPair.Value.EndUpdates;
if not(foTransient in lPair.Value.FieldOptions) then
// if not(foTransient in lPair.Value.FieldOptions) then
if lPair.Value.Writeable then
begin
Inc(fNonTransientFieldsCount);
Inc(fWritableFieldsCount);
end;
if lPair.Value.Readable then
begin
Inc(fReadableFieldsCount);
end;
end;
end;
@ -2781,8 +2814,18 @@ end;
procedure TFieldInfo.EndUpdates;
begin
Writeable := (not FieldName.IsEmpty) and (not((foAutoGenerated in FieldOptions) or (foTransient in FieldOptions)));
Readable := not(foTransient in FieldOptions) and (not FieldName.IsEmpty);
if FieldName.IsEmpty then
begin
Writeable := false;
Readable := false;
end
else
begin
// Writeable := (not (foReadOnly in FieldOptions)) and (not((foAutoGenerated in FieldOptions) or (foTransient in FieldOptions)));
Writeable := ((FieldOptions * [foReadOnly, foTransient, foAutoGenerated]) = []);
// Readable := (not (foWriteOnly in FieldOptions)) and (not(foTransient in FieldOptions));
Readable := (FieldOptions * [foWriteOnly, foTransient]) = [];
end;
end;
initialization

View File

@ -167,7 +167,14 @@ end;
function TRQLPostgreSQLCompiler.RQLLimitToSQL(const aRQLLimit: TRQLLimit): string;
begin
Result := Format(' /*limit*/ LIMIT %d OFFSET %d', [aRQLLimit.Count, aRQLLimit.Start]);
if aRQLLimit.Start = 0 then
begin
Result := Format(' /*limit*/ LIMIT %d', [aRQLLimit.Count]);
end
else
begin
Result := Format(' /*limit*/ LIMIT %d OFFSET %d', [aRQLLimit.Count, aRQLLimit.Start]);
end;
end;
function TRQLPostgreSQLCompiler.RQLLogicOperatorToSQL(const aRQLFIlter: TRQLLogicOperator): string;

View File

@ -91,6 +91,7 @@ var
lPKInInsert: Boolean;
begin
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
lSB := TStringBuilder.Create;
try
lSB.Append('INSERT INTO ' + TableName + '(');

View File

@ -63,6 +63,7 @@ var
lPKInInsert: Boolean;
begin
lPKInInsert := (not PKFieldName.IsEmpty); // and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
lSB := TStringBuilder.Create;
try
lSB.Append('INSERT INTO ' + TableName + '(');

View File

@ -88,6 +88,7 @@ var
lPKInInsert: Boolean;
begin
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
lSB := TStringBuilder.Create;
try
lSB.Append('INSERT INTO ' + TableName + '(');

View File

@ -88,6 +88,7 @@ var
lPKInInsert: Boolean;
begin
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
lSB := TStringBuilder.Create;
try
lSB.Append('INSERT INTO ' + TableName + '(');

View File

@ -99,6 +99,7 @@ var
lPKInInsert: Boolean;
begin
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
lSB := TStringBuilder.Create;
try
lSB.Append('INSERT INTO ' + GetTableNameForSQL(TableName) + ' (');
@ -146,6 +147,11 @@ function TMVCSQLGeneratorPostgreSQL.CreateSelectByPKSQL(
const Map: TFieldsMap; const PKFieldName: string;
const PKOptions: TMVCActiveRecordFieldOptions): string;
begin
if PKFieldName.IsEmpty then
begin
raise EMVCActiveRecord.Create('No primary key provided. [HINT] Define a primary key field adding foPrimaryKey in field options.');
end;
Result := CreateSelectSQL(TableName, Map, PKFieldName, PKOptions) + ' WHERE ' +
GetFieldNameForSQL(PKFieldName) + '= :' + GetParamNameForSQL(PKFieldName); // IntToStr(PrimaryKeyValue);
end;

View File

@ -87,6 +87,7 @@ var
lPKInInsert: Boolean;
begin
lPKInInsert := (not PKFieldName.IsEmpty) and (not(TMVCActiveRecordFieldOption.foAutoGenerated in PKOptions));
lPKInInsert := lPKInInsert and (not(TMVCActiveRecordFieldOption.foReadOnly in PKOptions));
lSB := TStringBuilder.Create;
try
lSB.Append('INSERT INTO ' + TableName + ' (');

View File

@ -736,7 +736,7 @@ var
Attrs: TArray<TCustomAttribute>;
Attr: TCustomAttribute;
begin
{ TODO -oDanieleT -cGeneral : in un rendering di una lista, quante volte viene chiamata questa funzione?}
{ TODO -oDanieleT -cGeneral : in un rendering di una lista, quante volte viene chiamata questa funzione? }
{ Tante volte, ma eliminando tutta la logica si guadagnerebbe al massiom il 6% nel caso tipico, forse non vale la pena di aggiungere una cache apposita }
Result := AProperty.Name;
@ -1006,6 +1006,7 @@ var
lInternalStream: TStream;
lSStream: TStringStream;
lValue: TValue;
lStrValue: string;
{$IF not Defined(TokyoOrBetter)}
lFieldValue: string;
{$ENDIF}
@ -1023,7 +1024,35 @@ begin
case AField.DataType of
ftString, ftWideString:
begin
aRTTIField.SetValue(AObject, AField.AsString);
// mysql tinytext is identified as string, but raises an Invalid Class Cast
// so we need to do some more checks...
case aRTTIField.FieldType.TypeKind of
tkString, tkUString:
begin
aRTTIField.SetValue(AObject, AField.AsString);
end;
tkClass: { mysql - maps a tiny field, identified as string, into a TStream }
begin
lInternalStream := aRTTIField.GetValue(AObject).AsObject as TStream;
if lInternalStream = nil then
begin
raise EMVCException.CreateFmt('Property target for %s field is nil. [HINT] Initialize the stream before load data', [AField.FieldName]);
end;
lInternalStream.Size := 0;
lStrValue := AField.AsString;
if not lStrValue.IsEmpty then
begin
lInternalStream.Write(lStrValue, Length(lStrValue));
lInternalStream.Position := 0;
end;
end
else
begin
raise EMVCException.CreateFmt('Unsupported FieldType (%d) for field %s',
[Ord(AField.DataType), AField.FieldName]);
end;
end;
// aRTTIField.SetValue(AObject, AField.AsString);
end;
ftLargeint, ftAutoInc:
begin