You are not logged in.
Hi Ab,
As my adventures in mORMot continues, I now sit with an interesting issue. This is probably a FAQ, but given the following:
type
TSupplier = class(TSynPersistent)
private
fName : RawUTF8;
published
property Name : RawUTF8 read fName write fName;
end;
TSuppliers = array of TSupplier;
ISuppliers = interface(IInvokable)
['{2BDC9F78-B9C2-4621-A557-F87F02AC0581}']
function GetSuppliers(const aLocationID: RawUTF8; out Suppliers: TSuppliers): TCQRSResult;
end;
When invoking the interface, Suppliers are serialized as an array of integer?
Changing
TSuppliers = TObjectList<TSupplier>;
Generates Access Violations.
I've also tried to call:
TJSONSerializer.RegisterObjArrayForJSON( TypeInfo(TStores), TStore);
Which generates an error claiming that I have to write a custom serializer for TStore.
Some direction will be appreciated.
Offline
Just an update:
1. Declaring TSuppliers as a descendant of TCollection works, but then I can't use TSupplier on it's own.
2. Declaring TSuppliers as a TObjectList serializes the array properly, but adds the ugly ClassName property.
Offline
If your TCollectionItem has the RTTI via {$M+}, you could it on its own, I suppose.
You could define
TSupplierObjArray = array of TSupplier;
Then register it via
TJSONSerializer.RegisterObjArrayForJSON(TypeInfo(TSupplierObjArray),TSupplier);
Use ObjArray*() functions to manage the dynamic array.
You could even define T*ObjArray published properties to any TPersistent - preferably a TSynAutoCreateFields which would free the array at Destroy - and serialize them.
It sounds to me easier to work with than a TCollection.
There is no built-in support of generic lists by now.
Perhaps in the future.
Offline
Hi Ab,
When the function call ends, I get an access violation in ObjArrayClear(Value^) (mORMot.pas[48685]) from what I traced it looks like it's part of the cleanup after the call was dispatched.
Perhaps I'm not initializing the Array correctly?
function TSupplierQuery.GetSuppliers(const aCompanyID: RawUTF8; out Suppliers: TSupplierObjArray): TCQRSResult;
var
Res : ISQLDBRows;
Supplier : TSupplier;
I : integer;
begin
Result := cqrsNotFound;
Res := fDbConnection.Execute( 'select count(*) from SUPPLIERS where COMPANY_ID=?', [aCompanyID] );
if Res.Step then begin
ObjArraySetLength( Stores, 0 ); //Res.ColumnInt(0) ); -- Ideally I'd like to pre-allocate the array
Res := fDbConnection.Execute( 'select SUPPLIER_ID from SUPPLIERS where COMPANY_ID=?', [aCompanyID] );
while Res.Step do begin
Supplier := TSupplier.Create;
GetSupplier(Res['SUPPLIER_ID'], Supplier);
ObjArrayAdd( Suppliers, Supplier );
end;
end;
end;
Offline
Reducing and simplifying to:
function TSupplierQuery.GetSuppliers(const aCompanyID: RawUTF8; out Suppliers: TSupplierObjArray): TCQRSResult;
var
Res : ISQLDBRows;
Supplier : TSupplier;
begin
Result := cqrsNotFound;
Res := fDbConnection.Execute( 'select SUPPLIER_ID from SUPPLIERS where COMPANY_ID=?', [aCompanyID] );
while Res.Step do begin
Supplier := TSupplier.Create;
GetSupplier(Res['SUPPLIER_ID'], Supplier);
ObjArrayAdd( Suppliers, Supplier );
end;
end;
Still produces an Access Violation.
Changing mORMot.pas[48685]:
if IsObjArray then
ObjArrayClear(Value^) else
Wrapper.Clear;
to
if false then//IsObjArray then
ObjArrayClear(Value^) else
Wrapper.Clear;
Works perfectly, but I'm pretty sure that will produce a memory leak.
Last edited by willo (2015-06-12 10:54:53)
Offline
In fact, Wrapper.Clear won't produce any memory leak, since TDynArray would identify the array as a T*ObjArray...
But it is faster to use ObjArrayClear() - but the implementation was broken.
Please try http://synopse.info/fossil/info/4318a15a6c
Sorry for the inconvenience.
Offline
while Res.Step do begin
Supplier := TSupplier.Create;
GetSupplier(Res['SUPPLIER_ID'], Supplier);
ObjArrayAdd( Suppliers, Supplier );
end;
The Array of Supplier, could be auto free memory by the framework?
if not,when the Array of Supplier should be free by manul?
Offline
In order to be able to use this kinda service, a factory is mandatory. Before, you have to explicity resolve the factory to get automatic injection, you need to register the magic factory via aServer.ServiceContainer.InjectResolver(..). Doing this, it would let the factory be owned by aServer.Services - the parent, so that way you would not need to manually release its instance - the parent will release for you.
Offline
@warleyalex thank you explain the detail of it!
Offline