Using Ancestor Class

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?


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.

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;


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.

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?


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.

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.


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.

Version 3.0.1 has been released with that feature: http://tmssoftware.biz/business/dmodeler/doc/web/adding-a-unit-name-to-the-uses.html

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.


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.

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=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.

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;

Yes! That's it.
It's working now, thanks for your support.