Web forum is in read-only mode. Login as active registered customer for write access
  Forum Search   New Posts New Posts

Using Ancestor Class

 Post Reply Post Reply
Author
Ronald Janse View Drop Down
Senior Member
Senior Member
Avatar

Joined: 20 Aug 2015
Posts: 265
Post Options Post Options   Quote Ronald Janse Quote  Post ReplyReply Direct Link To This Post Topic: Using Ancestor Class
    Posted: 19 Oct 2017 at 5:02pm
I have a field that is common to all tables, named userid.
I want to use an ancestor class like this:

TObjectWithUserID = class(TObject)
  userid: integer;
end;

All tables have a field userid of type integer.
In the Aurelius Export I put the classname TObjectWithUserID in the box 'Ancestor Class'.
On the 'Mappings' page I remove the checkmarks before the field userid of every table mapped.

That should work. The only thing missing is the ancestor class itself. Should I insert that manually every time I generate the datamodule for Aurelius, or am I missing something here?


Back to Top
Wagner R. Landgraf View Drop Down
TMS Support
TMS Support
Avatar

Joined: 18 May 2010
Posts: 2392
Post Options Post Options   Quote Wagner R. Landgraf Quote  Post ReplyReply Direct Link To This Post Posted: 19 Oct 2017 at 10:51pm
Hi Ronald,

Let's talk about Aurelius itself for a while and leave Data Modeler aside. You can't have mapped fields in non-entity classes. In other words, if your ancestor class TObjectWithUserID is not going to be persisted in a specific table, you should not have your userid property mapped to a table column using [Column] attribute. Even though you have that field in ancestor class, you should map it in each descendant table. Usually users do this:

TObjectWithUserID = class(TObject)
private
  FUserId: Integer;
public
  property UserId: Integer read FUserId write FUserId;
end;

TSomeClass = class(TObjectWithUserID)
public
  [Column('userid')]
  property UserId;
end;

That way you have the regular OOP inheritance (UserId property is in the ancestor) but your mapping is specific to each descendant object - because persistence of that field is conceptually done in different table columns. In theory you could have the userid property of class TClass1 being saved in Table1.UserId, and the same userid property of class TClass2 being saved in Table2.USER_ID.

Having said all that, TMS Data Modeler doesn't build that construction automatically, unfortunately. It maps fields directly (not properties). So you would have to fine tune the source code it generated. Note though that Data Modeler 3 has scripting capabilities that allow you to find tune the generated source code through Pascal script. 

In this case for example you could create a script that declares a property userid and adds the Column attributes to it for all Aurelius classes.
Back to Top
Ronald Janse View Drop Down
Senior Member
Senior Member
Avatar

Joined: 20 Aug 2015
Posts: 265
Post Options Post Options   Quote Ronald Janse Quote  Post ReplyReply Direct Link To This Post Posted: 20 Oct 2017 at 12:00pm
Hi Wagner,

I see. I tried to get the job done with the scripter in Datamodeller 3, but that's no easy task.
As far as I can see, it's not possible to add a [Column] statement at the right place. If there is, maybe you can show me.
I used the OnClassGenerated procedure to add the userid property. That works, but only in the 'standard' manner (with read and write statements).
And as I understand it I have to Add the declaration of TObjectWithUserID manually?
I have tried something like this:

procedure OnClassGenerated(Args: TClassGeneratedArgs);
begin
  Args.CodeType.AddProperty('userid', 'string',
          'Fuserid', 'Fuserid', mvPublic);
end;

Back to Top
Ronald Janse View Drop Down
Senior Member
Senior Member
Avatar

Joined: 20 Aug 2015
Posts: 265
Post Options Post Options   Quote Ronald Janse Quote  Post ReplyReply Direct Link To This Post Posted: 20 Oct 2017 at 3:44pm
I tried all kinds of statements, but I can't get the generated pas file right.
What doesn't help is that there is not much documentation or sourcecode to read. It's a new option of course, but I hope there will be more documented in the future.
I tried things like:

  Args.CodeUnit.Variables.
  Args.CodeType.AddAttribute.CustomAttributes.Add('userid');
  Args.CodeType.AddField('[Column(''userid'', [])]','', mvPublic);
  Args.CodeType.CustomAttributes.Add(...) 
  Args.CodeType.AddFunction('Testfunctie','integer',mvPublic);
  Args.CodeType.AddField('Fuserid', 'integer', mvPrivate);

most of them don't work, generate some error or appear on the wrong place.
I fear I will have to write a program to parse the generated .pas file everytime I have created a new one.
Back to Top
Wagner R. Landgraf View Drop Down
TMS Support
TMS Support
Avatar

Joined: 18 May 2010
Posts: 2392
Post Options Post Options   Quote Wagner R. Landgraf Quote  Post ReplyReply Direct Link To This Post Posted: 23 Oct 2017 at 4:13am
You can use the following code to create a new property in a class with an attribute:

procedure OnClassGenerated(Args: TClassGeneratedArgs);
var
  Field: TCodeMemberField;
begin                                     
  Field := Args.CodeType.AddField('Fuserid', 'string', mvPrivate);
  Field.AddAttribute('Column').AddRawArgument('''userid''');
  Args.CodeType.AddProperty('userid', 'string', 'Fuserid', 'Fuserid', mvPublic);
end;

About the TObjectWithUserID I think it would be easier to just declare it in another unit, and just reference it from the unit Data Modeler generates?


Back to Top
Ronald Janse View Drop Down
Senior Member
Senior Member
Avatar

Joined: 20 Aug 2015
Posts: 265
Post Options Post Options   Quote Ronald Janse Quote  Post ReplyReply Direct Link To This Post Posted: 23 Oct 2017 at 8:41am
Works great, thank you.
I think I can implement it now.
If you could find the time to add something about the way this works in a future version of the help, that would be very much appreciated. We have a huge datamodel and some finetuning of the script this way can come in very handy.
Back to Top
Wagner R. Landgraf View Drop Down
TMS Support
TMS Support
Avatar

Joined: 18 May 2010
Posts: 2392
Post Options Post Options   Quote Wagner R. Landgraf Quote  Post ReplyReply Direct Link To This Post Posted: 23 Oct 2017 at 11:52am
Yes, we intend to keep improving documentation. Feel free to ask though, customer feedback also drivers documentation, as we can include more examples that are often needed by users.
Back to Top
Ronald Janse View Drop Down
Senior Member
Senior Member
Avatar

Joined: 20 Aug 2015
Posts: 265
Post Options Post Options   Quote Ronald Janse Quote  Post ReplyReply Direct Link To This Post Posted: 01 Nov 2017 at 11:30am
Originally posted by Wagner R. Landgraf

You can use the following code to create a new property in a class with an attribute:

procedure OnClassGenerated(Args: TClassGeneratedArgs);
var
  Field: TCodeMemberField;
begin                                     
  Field := Args.CodeType.AddField('Fuserid', 'string', mvPrivate);
  Field.AddAttribute('Column').AddRawArgument('''userid''');
  Args.CodeType.AddProperty('userid', 'string', 'Fuserid', 'Fuserid', mvPublic);
end;

About the TObjectWithUserID I think it would be easier to just declare it in another unit, and just reference it from the unit Data Modeler generates?



Is it possible to add a unit to the uses clause in the script?
If that is possible I think I have everything I need. If not I will have to add it everytime I generate the Aurelius classes.
Back to Top
Wagner R. Landgraf View Drop Down
TMS Support
TMS Support
Avatar

Joined: 18 May 2010
Posts: 2392
Post Options Post Options   Quote Wagner R. Landgraf Quote  Post ReplyReply Direct Link To This Post Posted: 01 Nov 2017 at 1:12pm
Back to Top
Ronald Janse View Drop Down
Senior Member
Senior Member
Avatar

Joined: 20 Aug 2015
Posts: 265
Post Options Post Options   Quote Ronald Janse Quote  Post ReplyReply Direct Link To This Post Posted: 01 Nov 2017 at 1:19pm
Great! Just in time!
And I see you also added the alphabetical order to the Aurelius generator.
I tried it and it works great, thank you.
Back to Top
Ronald Janse View Drop Down
Senior Member
Senior Member
Avatar

Joined: 20 Aug 2015
Posts: 265
Post Options Post Options   Quote Ronald Janse Quote  Post ReplyReply Direct Link To This Post Posted: 01 Nov 2017 at 2:04pm
Originally posted by Wagner R. Landgraf

Hi Ronald,

Let's talk about Aurelius itself for a while and leave Data Modeler aside. You can't have mapped fields in non-entity classes. In other words, if your ancestor class TObjectWithUserID is not going to be persisted in a specific table, you should not have your userid property mapped to a table column using [Column] attribute. Even though you have that field in ancestor class, you should map it in each descendant table. Usually users do this:

TObjectWithUserID = class(TObject)
private
  FUserId: Integer;
public
  property UserId: Integer read FUserId write FUserId;
end;

TSomeClass = class(TObjectWithUserID)
public
  [Column('userid')]
  property UserId;
end;

That way you have the regular OOP inheritance (UserId property is in the ancestor) but your mapping is specific to each descendant object - because persistence of that field is conceptually done in different table columns. In theory you could have the userid property of class TClass1 being saved in Table1.UserId, and the same userid property of class TClass2 being saved in Table2.USER_ID.

Having said all that, TMS Data Modeler doesn't build that construction automatically, unfortunately. It maps fields directly (not properties). So you would have to fine tune the source code it generated. Note though that Data Modeler 3 has scripting capabilities that allow you to find tune the generated source code through Pascal script. 

In this case for example you could create a script that declares a property userid and adds the Column attributes to it for all Aurelius classes.

Wagner, still one loose end:
I have implemented everything as you said and with the sourcecode that seems to work great, except from one thing: I can't declare the public column and property the way you state above.
Back to Top
Wagner R. Landgraf View Drop Down
TMS Support
TMS Support
Avatar

Joined: 18 May 2010
Posts: 2392
Post Options Post Options   Quote Wagner R. Landgraf Quote  Post ReplyReply Direct Link To This Post Posted: 01 Nov 2017 at 8:55pm
Another update has been released. When creating a property, you can pass an empty string as the property type. In that case, it will create the property declaration with just the name.
Back to Top
Ronald Janse View Drop Down
Senior Member
Senior Member
Avatar

Joined: 20 Aug 2015
Posts: 265
Post Options Post Options   Quote Ronald Janse Quote  Post ReplyReply Direct Link To This Post Posted: 02 Nov 2017 at 8:47am
[QUOTE=Wagner R. Landgraf]Another update has been released. When creating a property, you can pass an empty string as the property type. In that case, it will create the property declaration with just the name.[/QUOTE
Thank you for the swift updater.
Tried that and it works as you said. That's great.
The very last thing I still need though is a way to create a column, which is necessary as you pointed out earlier.
I can only create a column as part of a field, but I don't create a field, because I use the ancestorclass.
You told me I need this:
public
  [Column('userid')]
  property UserId;
end;

I tried that by changing the Aurelius export by hand and it works like it should. I just need a way to create the Column now.
Back to Top
Wagner R. Landgraf View Drop Down
TMS Support
TMS Support
Avatar

Joined: 18 May 2010
Posts: 2392
Post Options Post Options   Quote Wagner R. Landgraf Quote  Post ReplyReply Direct Link To This Post Posted: 03 Nov 2017 at 2:33pm
Just create the property and add the attribute the same way you did with the field:

var
  Prop: TCodeMemberProperty;
begin                                     
  Prop :=   Args.CodeType.AddProperty('UserId', '', '', '', mvPublic);
  Field.AddAttribute('Column').AddRawArgument('''userid''');
end;
Back to Top
Ronald Janse View Drop Down
Senior Member
Senior Member
Avatar

Joined: 20 Aug 2015
Posts: 265
Post Options Post Options   Quote Ronald Janse Quote  Post ReplyReply Direct Link To This Post Posted: 03 Nov 2017 at 2:52pm
Yes! That's it.
It's working now, thanks for your support.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down