Hi, okay, let's see if I can make it easier.
This is my Aurelius-mapped class (note: this comes straight from the TMS Data Modeler):
[Entity]
[Table('VRTAMBITI')]
[UniqueKey('DESCRIZIONE')]
[Id('FID', TIdGenerator.IdentityOrSequence)]
TVRTAMBITI = class
private
[Column('ID', [TColumnProp.Required, TColumnProp.NoInsert, TColumnProp.NoUpdate])]
FID: Integer;
[Column('DESCRIZIONE', [TColumnProp.Required], 50)]
[MinLength(10,'Servono almeno 10 caratteri')]
FDESCRIZIONE: string;
[Column('USER_INS', [], 50)]
FUSER_INS: Nullable<string>;
[Column('DATA_INS', [])]
FDATA_INS: Nullable<TDateTime>;
[Column('USER_UPD', [], 50)]
FUSER_UPD: Nullable<string>;
[Column('DATA_UPD', [])]
FDATA_UPD: Nullable<TDateTime>;
public
property ID: Integer read FID write FID;
property DESCRIZIONE: string read FDESCRIZIONE write FDESCRIZIONE;
property USER_INS: Nullable<string> read FUSER_INS write FUSER_INS;
property DATA_INS: Nullable<TDateTime> read FDATA_INS write FDATA_INS;
property USER_UPD: Nullable<string> read FUSER_UPD write FUSER_UPD;
property DATA_UPD: Nullable<TDateTime> read FDATA_UPD write FDATA_UPD;
end;
The connection is currently created like so:
function TSettings.GetConnection: IDBConnection;
begin
Result := TMSSQLConnection.Create(FSQLServerSettings.ToString);;
end;
I changed it to this just to understand if there was some kind of conflict, but it makes no difference.
Then we have a set of generic interfaces IEntityLoader, IEntitySaver, etc. that accept any class with corresponding objects and then we have the TAureliusXXX that map everything onto Aurelius:
TAureliusLoader<T:class> = class( TEntityLoader<T> )
strict private
FObjectManager: TObjectManager;
strict protected
procedure Load(ID: integer);override;
property ObjManager: TObjectManager read FObjectManager;
public
constructor Create( AObjManger: TObjectManager );overload;
destructor Destroy; override;
end;
TAureliusSaver<T:class> = class( TEntitySaver<T> )
strict private
FObjectManager: TObjectManager;
strict protected
procedure Save(Entity: T); override;
procedure Save(Entities: TArray<T>);override;
property ObjManager: TObjectManager read FObjectManager;
public
constructor Create( AObjManger: TObjectManager );overload;
destructor Destroy; override;
end;
TAureliusDeleter<T:class> = class( TBFiveBOInterfacedObject, IEntityDelete<T> )
strict private
FObjectManager: TObjectManager;
strict protected
procedure Delete( Entity: T );overload;
procedure Delete( Entities: TArray<T> );overload;
property ObjManager: TObjectManager read FObjectManager;
public
constructor Create( AObjManger: TObjectManager );overload;
destructor Destroy; override; 00
end;
The code for these is that which you probably expect: the create accepts an object manager and then Load, Save and Delete do the right thing respectively, in this way:
procedure TAureliusLoader<T>.Load(ID: integer);
begin
inherited;
Entity := ObjManager.Find<T>(Id);
end;
procedure TAureliusSaver<T>.Save(Entities: TArray<T>);
begin
FObjectManager.UseTransactions := True;
inherited Save(Entities);
end;
procedure TAureliusSaver<T>.Save(Entity: T);
begin
inherited;
ObjManager.SaveOrUpdate(Entity);
end;
procedure TAureliusDeleter<T>.Delete(Entities: TArray<T>);
var
Entity: T;
begin
FObjectManager.UseTransactions := True;
for Entity in Entities do
begin
Delete(Entity);
end;
end;
procedure TAureliusDeleter<T>.Delete(Entity: T);
begin
if Assigned( Entity ) then
ObjManager.Remove(Entity);
end;
Save works just like you expect when you're adding a new item but the get trips everything up when you have an existing item,
Delete also does not work as intended but throws a weird message out:
Project BFiveServiceVCL.exe raised exception class EObjectAlreadyDetached with message 'Cannot remove or detach object of class TVRTAMBITI. The object is not in persistent context.'.
The code for the delete in the service is as follows:
procedure TAmbitiService.Ambito_DELETE(ID: integer;
ResultStatus: TResultStatus);
var
ResultObj: IEntityLoad<TVRTAMBITI>;
begin
with IIntegerValidation( TPositiveIntValidation.Create ) do
begin
Candidate := ID;
if not Validate then
begin
ResultStatus.Error(RESULT_CODE_REQUIRED_PARAMETERS_MISSING, SessionUser.UserLocale, 'id');
Exit;
end;
end;
try
ResultStatus.Error(RESULT_CODE_ITEM_NOT_FOUND, SessionUser.UserLocale);
ResultObj := IEntityLoad<TVRTAMBITI>(TAmbitoLoad.Create(NewObjectManager));
ResultObj.Load(ID);
IEntityDelete<TVRTAMBITI>( TAmbitoDelete.Create( NewObjectManager ) ).Delete(ResultObj.Entity);
ResultStatus.Success;
except
on E: Exception do
begin
HandleException(E, ResultStatus);
end;
end;
end;
As you can see we first load the object and then delete it. To my mind, the fact we're loading the object puts it in the persistent context. Is that not so?
Yet the get is what baffles me the most because it sometimes works and sometimes doesn't.
This is the service code:
procedure TAmbitiService.Ambito_GET(ID: integer;
ResultStatus: TResultStatus; Result: TAmbito);
var
ResultObj: IEntityLoad<TVRTAMBITI>;
begin
try
ResultObj := IEntityLoad<TVRTAMBITI>(TAmbitoLoad.Create(NewObjectManager));
ResultObj.Load(ID);
Result := TAmbito.Create(nil);
Result.id := ResultObj.Entity.id;
Result.Descrizione := ResultObj.Entity.Descrizione;
if not ResultObj.Entity.USER_INS.IsNull then
Result.DataLog.UserIns := ResultObj.Entity.USER_INS.Value;
if not ResultObj.Entity.DATA_INS.IsNull then
Result.DataLog.DataIns := DateTimeToUnix(ResultObj.Entity.DATA_INS.Value);
if not ResultObj.Entity.USER_UPD.IsNull then
Result.DataLog.UserUpd := ResultObj.Entity.USER_UPD.Value;
if not ResultObj.Entity.DATA_UPD.IsNull then
Result.DataLog.DataUpd := DateTimeToUnix( ResultObj.Entity.DATA_UPD.Value );
if Not Assigned( Result ) then
ResultStatus.Error(RESULT_CODE_ITEM_NOT_FOUND, SessionUser.UserLocale, Int32ToUtf8(ID));
except
on E: Exception do
begin
ResultStatus.Error(RESULT_CODE_EXCEPTION_TRAPPED, SessionUser.UserLocale, UnicodeStringToUtf8(E.Message));
raise;
end;
end;
if not Assigned( Result ) then
ResultStatus.Error(RESULT_CODE_ITEM_NOT_FOUND, SessionUser.UserLocale);
end;
When it does not work, it'll sometimes throw out the message that the ID is not mapped (which is a lie) and sometimes the duplicates not allowed in the internal Aurelius dictionaries as soon as I try to get a new object manager. I wonder if I should not be freeing the object manager, which I currently do in the destroy if it's assigned.
When no error is generated server side, however, when I close the server my ReportOnMemoryLeaks := True shows this:
I can't make heads or tails of what exactly the problem is because it shows in so many different ways. There may be more than one problem and even then I am at a loss as to what they may be.
Thanks!