Through Data Modeler Aurelius script events, how can we add, remove or change code of an entity constructor, destructor or other existing methods?

  1. How can I access constructor &/or destructor of an entity so that I could add/remove/change code ?
    1.1) How could I access and add/remove/change code of any other existing method, such as the getter and setter functions/procedures?
    Is it accessible through Members collection? If so, how to use it?

  2. How could I create a function/procedure and add var declarations, once the AddSnippet procedure works inside a default begin/end block? Does this Statements property do the job? If so, how?

  3. How could I add a constructor/destructor to an entity which didn't had them automatically created?

I need to access constructor and destructor methods because I create proxy fields in OnClassGenerated event that I should initialize and destroy (SetInitialValue / DestroyValue), so I noticed there's no "FindFunction", "FindProcedure", or a creator/destructor code access method - there are only AddFunction and AddProcedure methods.

  1. Maybe these documentation topics can help you out:

  1. You can try this:
Func.AddParameter('Value', 'double').Modifier := pmVar;
  1. You can iterate through Members property to find the corresponding TCodeMemberMethod object representing a method.

I meant "how to create local variables" on these procedures/functions.

AddParameter will add parameters to the method; the question was about inner local var declarations, as the AddSnippet puts code between an automatically generated begin/end block.
I asked if we could insert declarations using this Statements collection, but now I understand we can't.
1-Through code completion I could not see a TCodeStatement property that access the source code text
2-I can access Statements.Count, and watching them I see it does not sum the automatically created begin/end as statements, so although I used this collection, I wouldn't be able to add code before the initial " begin "

It's know that with new Delphi versions we could use inline variable declaration, but Code Insight still gets a little crazy when you use this, so I'd like to avoid it in case I needed local variables.

Indeed, there is no way to create local variables. But I'd recommend you do not keep your script too complex and/or add too much code there, it's harder to maintain.

The approach I'd suggest is that you just create a separate unit with the more complex code, and from your entity classes just do simple calls to such methods. Thus it's easier for you to maintain the extra code and logic, and your scripts and generate code stays simple.

I understand ...
I really don't want the scripts to be too complex. I don't have a real case where I need local variables now.

But I really do have a worth case that I would need at least functions to access constructors and destructors procedures, such as AddConstructor and and AddDesctructor.

I have a ManyValuedAssociation that I would replicate to 75% of my model entities. So, in order not to make modeling too complex by manually adding those relationships, I add a Field & Property by code.
In this case it's a Proxy<TList> field, so I should call SetInitialValue and DestroyValue in constructor and destructor.

Through the members collection I could "brute-force-find" these procedures and try to add snippets, but I have a huge problem when it's a type that originally doesn't have a constructor. There's no AddConstructor implementation.

This is my code.

  with Args.CodeType.AddField('FObjItens', 'Proxy<TList<TObjItem>>', mvPrivate) do begin
    AddAttribute('ManyValuedAssociation').AddRawArgument('[TAssociationProp.Lazy], CascadeTypeAllRemoveOrphan');
    AddAttribute('ForeignJoinColumn').AddRawArgument('''id_entidade'',[TColumnProp.Required]');
  end;                                            
  Args.CodeType.AddProperty('ObjItens', 'TList<TObjItem>', 'GetObjItens', 'SetObjItens', mvPublic);
  Args.CodeType.AddFunction('GetObjItens', 'TList<TObjItem>', mvPrivate).AddSnippet('Result := FObjItens.Value;');
  with Args.CodeType.AddProcedure('SetObjItens', mvPrivate) do begin
    AddParameter('Value', 'TList<TObjItem>');
    AddSnippet('FObjItens.Value := Value;');
  end;

Ok creating the field and the property, but I cannot create/access the class constructor to initialize those fields, neither create/access the class destructor to destroy them.

Wouldn't accessing and/or creating constructor / destructor qualify as a new feature candidate?

I guess I found a way meanwhile!

    with Args.CodeType.AddProcedure('AfterConstruction', mvPublic) do begin
      AddSnippet('FObjItens.SetInitialValue(TList<TObjItem>.Create);');
    end;
    with Args.CodeType.AddProcedure('BeforeDestruction', mvPublic) do begin
      AddSnippet('FObjItens.DestroyValue;');
    end;

For now, with these procedures I could apply FObjItens to some classes, and get it's .Count with no errors, and could see through an OnSQLExecuting that the query is properly done. I'll make more tests populating those tables and running more queries and creating/destroying instances. If something goes wrong, I'll let you know.

The only downside I could see till now is that it generates method hiding warnings during compilation, as I cannot programmatically add reintroduce or override to it's declaration.

Actually you can set CodeMethodDirectives to set the method as override:

  with Args.CodeType.AddProcedure('AfterConstruction', mvPublic) do begin
      Directives := SetOf([mdOverride]);
      AddSnippet('FObjItens.SetInitialValue(TList<TObjItem>.Create);');
    end;

To create a constructor, use this code:

  result := TCodeMemberConstructor.Create;
  result.Name := 'Create';
  result.Visibility := mvPublic;
  AType.Members.Add(result);
1 Like

Great! Thanks!

    LConstructor := FindConstructor(Args.CodeType);
    if LConstructor = nil then begin
      LConstructor            := TCodeMemberConstructor.Create;
      LConstructor.Name       := 'Create';
      LConstructor.Visibility := mvPublic;
      LConstructor.AddSnippet('inherited;');
      Args.CodeType.Members.Add(LConstructor);
    end;
    LConstructor.AddSnippet('FObjItens.SetInitialValue(TList<TObjItem>.Create);');
    
    LDestructor := FindDestructor(Args.CodeType);
    if LDestructor = nil then begin                     
      LDestructor            := TCodeMemberDestructor.Create;
      LDestructor.Name       := 'Destroy';
      LDestructor.Visibility := mvPublic;
      LDestructor.Directives := SetOf([mdOverride]);
      Args.CodeType.Members.Add(LDestructor);
    end else     
      // Delete previous generated "inherited" call
      LDestructor.Statements.Delete(LDestructor.Statements.Count - 1);
    LDestructor.AddSnippet('FObjItens.DestroyValue;');
    LDestructor.AddSnippet('inherited;');

(FindConstructor & FindDestructor iterate Members collection looking for a TCodeMemberXXXXXXX item).

1 Like

This topic was automatically closed 60 minutes after the last reply. New replies are no longer allowed.