Hi Wagner
I have a situation, with database update.
The tools I'm using: Delphi 10.2, firebird, Aurelius (last version), Datamodeler (last version).
I start by generating Schema with Datamodeler "Schema.pas", then I run the update routine,
something like:
procedure UpdateDatabase(vConnection: IDBConnection; aDBVersion: Integer);
var
DBManager: TDatabaseManager;
fFDScript: TFDScript;
begin
if version= 18 then
begin
..
aFDScript.SQLScripts[0].SQL.Add('ALTER TABLE Ent_Pro ALTER COLUMN Canceled SET NOT NULL;');
...
DBManager := TDatabaseManager.Create(vConnection);
DBManager.UpdateDatabase;// Update database with Schema.pas (Version 18)????
...
aFDScript.SQLScripts[0].SQL.Add('INSERT INTO SETTING (ID, KEYSECTION, KEYFIELD, KEYVALUE, KEYVALUETYPE, RECVERSION) VALUES (Gen_Id(ID_SETTING,1), ''Section 1'', ''Code'', ''0'', 1, current_timestamp)');
aFDScript.SQLScripts[0].SQL.Add('ALTER TABLE InvItem ALTER TaxPercent SET DEFAULT 0' );
...
end;
if version= 19 then
begin
...
aFDScript.SQLScripts[0].SQL.Add('ALTER TABLE Item ALTER COLUMN Enabled SET NOT NULL;');
...
DBManager := TDatabaseManager.Create(vConnection);
DBManager.UpdateDatabase;// // Update database with Schema.pas (Version 19)????
...
aFDScript.SQLScripts[0].SQL.Add('ALTER TABLE Item ALTER Enabled SET DEFAULT 0' );
...
end;
if version= 20 then
begin
...
DBManager.UpdateDatabase;// Update database with Schema.pas (Version 20)????
...
end;
end;
I need to save "schema.pas" for all versions, so when upgrade from version V17 to V20, can run
"DBManager.UpdateDatabase" for V18, V19, V20 schema. At this point I only have the last schema,
in this case only the V20, any sugestion how I can update several versions?
any idea how to save multiple versions of schema?
Thanks
You can use multimodel design, where you generate the Schema marking all classes with a specific model attribute, for example [Model('Schema18')].
DBManager := TDatabaseManager.Create(vConnection, TMappingExplorer.Get('Schema18'));
You can fill in the model name individually in the "advanced" tab (there is a field for Model).
procedure OnClassGenerated(Args: TClassGeneratedArgs);
var
Attr: TCodeAttributeDeclaration;
begin
Attr := Args.CodeType.AddAttribute('Model');
Attr.AddRawArgument('''' + 'Schema18' + '''');
end;
end;
"You can fill in the model name individually in the "advanced" tab (there is a field for Model)."
R: not a option, 300 Tables
"Or you can update automatically from a diagram name (create a diagram named Schema18 e put all tables there), ..."
R: not a option, 300 Tables. And same schema have different diagrams, for instance, Invoice Diagram, Customer Diagram, ....
"Or use customization script to add a Model attribute from the script. ..."
R: The attribute is add OK, but... i just don't understand Wagner, but i add schema18.pas, schema19.pas and schema20.pas
with same classes to the project?
Sorry...
Thanks
Hi, Wagner
Consider:
//Old Schema
unit DB.Schema;
...
[Entity]
[Table('Tabel1')]
[Id('FId', TIdGenerator.None)]
TTabel1 = class
private
[Column('Id', [TColumnProp.Required])]
FId: Integer;
[Column('Field1', [], 50)]
FField1: Nullable<string>;
public
property Id: Integer read FId write FId;
property Field1: Nullable<string> read FField1 write FField1;
end;
//New Schema
unit DB.Schema20;
...
[Entity]
[Table('Tabel1')]
[Id('FId', TIdGenerator.None)]
[Model('Schema20')]
TTabel1 = class
private
[Column('Id', [TColumnProp.Required])]
FId: Integer;
[Column('Field1', [], 50)]
FField1: Nullable<string>;
[Column('Field1', [], 50)]
FNewFieldSchema20: Nullable<string>;
public
property Id: Integer read FId write FId;
property Field1: Nullable<string> read FField1 write FField1;
end;
...
var
Schema20: TArray<TClass>;
...
Schema20 = TMappingExplorer.Get('Schema20');
"DBManager := TDatabaseManager.Create(vConnection, Schema20);"
...
what Schema is returned, DBSchema.pas or DBSchema20.pas? in this case Schema20 is empty!
i think Get('Schema20') is "looking" in wrong unit, Schema.pas instead of Schema20.pas
what i'm missing?
Many thanks
TMappingExplorer.Get doesn't return a TArray<TClass>. That code should not even compile, so how are you being able to run it?
You need to make sure all classes are registered with RegisterEntity otherwise if they are not used anywhere, linker will remove it from final executable.
Wagner R. Landgraf2018-11-12 21:02:40
Can you please try to reduce your existing code to a compilable project with SQLite and send to us? We will then send the project back to you, working.
Hi Wagner
You could alter AureliusGettingStarted sample:
Add Units to the project:
unit EntitiesV2;
interface
uses Aurelius.Mapping.Attributes ;
type
[Entity]
[Automapping]
[Model('EntitiesV2')]
TPerson = class
private
FId: integer;
FLastName: string;
FFirstName: string;
FEmail: string;
FNewFieldV2: string;
function GetFullName: string;
public
property Id: integer read FId;
property LastName: string read FLastName write FLastName;
property FirstName: string read FFirstName write FFirstName;
property Email: string read FEmail write FEmail;
property NewFieldV2: string read FNewFieldV2 write FNewFieldV2;
public
property FullName: string read GetFullName;
end;
implementation
{ TPerson }
function TPerson.GetFullName: string;
begin
Result := FirstName + ' ' + LastName;
end;
initialization
// RegisterEntity(TPerson);
end.
unit EntitiesV3;
interface
uses Aurelius.Mapping.Attributes;
type
[Entity]
[Automapping]
[Model('EntitiesV3')]
TPerson = class
private
FId: integer;
FLastName: string;
FFirstName: string;
FEmail: string;
FNewFieldV2: string;
FNewFieldV3: string;
function GetFullName: string;
public
property Id: integer read FId;
property LastName: string read FLastName write FLastName;
property FirstName: string read FFirstName write FFirstName;
property Email: string read FEmail write FEmail;
property NewFieldV2: string read FNewFieldV2 write FNewFieldV2;
property NewFieldV3: string read FNewFieldV3 write FNewFieldV3;
public
property FullName: string read GetFullName;
end;
implementation
{ TPerson }
function TPerson.GetFullName: string;
begin
Result := FirstName + ' ' + LastName;
end;
initialization
// RegisterEntity(TPerson);
end.
then alter:
procedure TForm1.btUpdateSchemaClick(Sender: TObject);
var
Setup: TMappingSetup;
fMappedClasses: TArray<TClass>;
fClass: TClass;
begin
//Update/Create Version 1*************
fMappedClasses := TMappedClasses.GetModelClasses('Default');
Setup := TMappingSetup.Create;
try
Setup.MappedClasses.RegisterClasses(TMappedClasses.GetModelClasses('Default'));
// TMappingExplorer.ReplaceDefaultInstance(TMappingExplorer.Create(Setup));
finally
Setup.Free;
end;
TDatabaseManager.Update(Connection);
ShowMessage('Database updated V1.');
//Update Version 2*************
fMappedClasses := TMappedClasses.GetModelClasses('EntitiesV2');
for fClass in fMappedClasses do
begin
fClass.ClassName; //fMappedClasses is empty!
end;
Setup := TMappingSetup.Create;
try
Setup.MappedClasses.RegisterClasses(TMappedClasses.GetModelClasses('EntitiesV2'));
TMappingExplorer.ReplaceDefaultInstance(TMappingExplorer.Create(Setup));//needed?
finally
Setup.Free;
end;
TDatabaseManager.Update(Connection);
ShowMessage('Database updated V2.');
//Update Version 2*************
fMappedClasses := TMappedClasses.GetModelClasses('EntitiesV3');
for fClass in fMappedClasses do
begin
fClass.ClassName; //fMappedClasses is empty!
end;
Setup := TMappingSetup.Create;
try
Setup.MappedClasses.RegisterClasses(TMappedClasses.GetModelClasses('EntitiesV3'));
TMappingExplorer.ReplaceDefaultInstance(TMappingExplorer.Create(Setup));//needed?
finally
Setup.Free;
end;
TDatabaseManager.Update(Connection);
ShowMessage('Database updated V3.');
end;
Thanks
As I have mentioned before: "You need to make sure all classes are registered with RegisterEntity otherwise if they are not used anywhere, linker will remove it from final executable.".
// RegisterEntity(TPerson);
end.
If a class is not used anywhere, linker will remove it from final executable. GetModelClasses can't return a class that doesn't exist in executable.
Wagner R. Landgraf2018-11-19 19:13:24
Back to the first post,...
Could you please uncomment the calls to RegisterEntity? That's probably the cause of the problems.
Hi, Wagner
Since Version 4.4:
...
Fixed : It's now possible to have two Aurelius entity class with same name in the same model (e.g, TCity in Unit1 and TCity in Unit2).
...
Reopen this post :)
Considere the same sample, with uncomment "initialization RegisterEntity(TPerson)", and adding some lines to
procedure TForm1.btSavePersonClick(Sender: TObject);
var
Person: TPerson;
fManager: TObjectManager;
fSetup: TMappingSetup;
fMappingExplorer: TMappingExplorer;
fEntities: String;
begin
fEntities := 'Entities';
fSetup := TMappingSetup.create;
try
fSetup.MappedClasses.RegisterClasses(TMappedClasses.GetModelClasses(fEntities));
fMappingExplorer := TMappingExplorer.Create(fSetup);
finally
fSetup.Free;
end;
//Creating a manager to MappingExplorer "Entities"
fManager := TObjectManager.Create(Connection, fMappingExplorer);
Person := fManager.Find<TPerson>(1);
...
end;
Exception
"raised exception class EClassNotRegistered with message 'Class TPERSON is not registered. Register it in the MappedEntities.'."
if remove Entities2 and Entities3, from the project, works as aspected.
if fEntities = 'EntitiesV3', works as aspected.
What is wrong?
Thanks