Jump to content
Stéphane Wierzbicki

RTTI: How can I SetValue or ReadValue on Record fields type

Recommended Posts

Hello everyone,

 

I hope this message finds you all in good health! This year and really weird.... But anyway, let's get back to the point.

I recently found a little nugget on the Internet developed by the brilliant @Uwe Raabe : it's a Dataset helper that automatically fills in the properties (or even fields) of an object. The source code can be found at this address :

https://www.uweraabe.de/Blog/2017/02/09/dataset-enumerator-reloaded/

 

I have slightly adapted the source code to use TMS Aurelius attributes (using "Column" attribute instead of "DBField" one provided bu Uwe). The Dataset helper works perfectly well "simple" classes such 


 

  TCustomer = class
  private
    [Column('CustNo')]
    FCustNo: Double;
    FCompany: string;

   FAddress1: string;
  public
    [DBField('Addr1')]
    property Address1: string read FAddress1 write FAddress1;
    property Company: string read FCompany write FCompany;
  end;

 

But it gets more difficult as soon as my fields are declared this way:

 

  TCustomer = class
  private
    [Column('CustNo')]
    FCustNo: Nullable<Double>;;
    FCompany: Nullable<String>;;
    FAddress1: string;
    FEntity: TObject;
  public
    [DBField('Addr1')]
    property Address1: string read FAddress1 write FAddress1;
    property Company: string read FCompany write FCompany;
    property Entity: TObject read FEntity write FEntity;
  end;

 

At runtime application is throwing "Invalid Type Typecast" exception on every TRTTI.GetValue or TRRTI.SetValue

 

procedure TDataSetHelper.TDataSetRecord<T>.TFieldMapping.LoadFromField(var Target: T);
begin
 FRTTIField.SetValue(GetPointer(Target), TValue.FromVariant(FField.Value));
end;

procedure TDataSetHelper.TDataSetRecord<T>.TFieldMapping.StoreToField(var Source: T);
begin
  FField.Value := FRTTIField.GetValue(GetPointer(Source)).AsVariant;
end;

procedure TDataSetHelper.TDataSetRecord<T>.TPropMapping.StoreToField(var Source: T);
begin
  FField.Value := FRTTIProp.GetValue(GetPointer(Source)).AsVariant;
end;

procedure TDataSetHelper.TDataSetRecord<T>.TPropMapping.LoadFromField(var Target: T);
begin
  FRTTIProp.SetValue(GetPointer(Target), TValue.FromVariant(FField.Value));
end;

I guess this is happening because of either reading/writing values from a Record or an Object.

I searched (maybe not enough nor thoroughly) the Internet but I wasn't able to solve this by myself.

 

Can someone helps me sorting this out adapting attached unit to handle simple properties types (integer, double, date, string, boolean....) values as well Nullable one ! (and rejects complex types too 🙂 )

I'll really appreciate some help.

 

Kind regards,

 

Stéphane

 

Ps: I have attached modified Uwe's unit file.

Ps2: I'm working with Delphi Sydney

 

 

 

Aurelius.Dataset.helper.pas

Share this post


Link to post

Nullable<> is a record, you have to get its (record)value, do your job on it (assign the values to this fetched record), then write it back to the field/property.

 

Share this post


Link to post
1 minute ago, Attila Kovacs said:

Nullable<> is a record, you have to get its (record)value, do your job on it (assign the values to this fetched record), then write it back to the field/property.

 

Thank you Attila, but I've already spotted this 😉. The thing is I don't know actually how to do this 🙂 !

Share this post


Link to post
5 minutes ago, Stéphane Wierzbicki said:

the RTTI code is not able to read (or write) value from (or to) a record.

Indeed, that is currently a known limitation.

Share this post


Link to post

I'm curious how would you write values to a "record" without knowing its implementation details.

Through parsing and validating its class operators?

Anyway, if you look after "delphi rtti record fields", you will find plenty of hits how to do it. It's pretty simple.

Share this post


Link to post

In the case of TMS Aurelius Nullable type, specifically, there are helper methods for you to use that do the job for you. It's TMappingExplorer.GetMemberValue and SetMemberValue. Here is some info:

https://support.tmssoftware.com/t/how-to-get-a-property-by-its-name/7777/2

 

But in general, you do what @Attila Kovacs mentioned: get the whole record, modify its fields via RTTI, and then set the record back to the field/property. That's what Aurelius does.

Share this post


Link to post
2 hours ago, Wagner Landgraf said:

In the case of TMS Aurelius Nullable type, specifically, there are helper methods for you to use that do the job for you. It's TMappingExplorer.GetMemberValue and SetMemberValue. Here is some info:

https://support.tmssoftware.com/t/how-to-get-a-property-by-its-name/7777/2

 

But in general, you do what @Attila Kovacs mentioned: get the whole record, modify its fields via RTTI, and then set the record back to the field/property. That's what Aurelius does.

Thanks, looks like I need either some good sleep or vacation !

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×