You are not logged in.
Assume that I have a record defined as:
TBaby = class
private
fName: RawUTF8;
fYear: Integer;
fSex: RawUTF8;
published
property Name: RawUTF8 read fName write fName;
property Year: Integer read fYear write fYear;
property Sex: RawUTF8 read fSex write fSex;
end;
TSQLBaby = class(TSQLRecord)
private
fBaby: TBaby;
published
property Baby: TBaby read fBaby write fBaby;
end;
Normally when TSQLBaby is stored in the database, the field "Baby" will contain JSON text, for example:
{"Name":"Jim","Year":2010,"Sex":"M"}
I'd like to be able to control which of the published fields get stored in the database on TSQLBaby.Add or TSQLBaby.Update
Is there a way to store (depending on some program logic) only subset of the published properties?
I'd like, for example, to omit "sex" for certain records and have some babies stored as:
{"Name":"Ann","Year":2014}
Can you please let me know if this is possible?
Thank you.
Offline
AFAIK It's not possible with the current version.
I guess that can be achieved by using attributes (http://wiki.freepascal.org/Property_attributes), which is also supported by FPC (). But I'm afraid Arnaud will have to abandon support for earlier versions of Delphi and Kylix, but I doubt he will.
Another idea would be allow defining a class method for TSQLRecord, something like TSQLRecord.GetAutoPersistedFieldsInCSVFormat(), where people can return a CSV-formatted string like 'ID, Name, Year', to define which fields will be handled by the framework. But I'm not sure if that will work, ab can tell us
Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.
Offline
You may simply use the persistence mapping as implemented for DDD Aggregates.
Offline
Thanks for the answers!
Sorry, I might not be knowledgeable in the entire framework, but I'm trying to learn
I studied the DDD chapter, but so far I don't see how I can may it work with my specific request.
DDD to ORM mapping (as far as I understand) suggests to "flatten" the structure instead of serializing object to JSON and storing it in a single field, but this is exactly what I want to avoid and I'd like to maintain the aggregate (JSON) storage in a single field.
Besides, I think that mapping between DDD and ORM is static, while in my case I want specifically "dynamic" mapping, i.e. selective subset of stored properties, individual for every record.
If I totally misunderstood the suggestion - can you please give more details or point me to the right direction and perhaps recommend some sample I can study?
Offline
So take a look at the TDocVariant type in our documentation, and use "variant" fields to store such "dynamic" content.
You may be amazed by the result: a NoSQL schemaless power in a regular SQL database...
Offline
ok, thanks. This part I understand perfectly - and I see how to do this with "variant" field. I was just looking for a simpler way. My object has several dozens of published properties, and saving it via TDocVariant is more pain than using standard ObjectToJSON -> TJSONSerializer.
so I really was looking to find something similar to what edwinsn suggested or some "callback" function in TSQLRecord which would ask me to "approve" or "deny" each stored property of any processed object.
I'd like also briefly explain why this scenario makes sense to me: in my app, when I instantiate the object, it is created with the "default" values for each property. The defaults are controlled by application, and user may customize them. When user recovers the "stored" object, it is essential (in some specific cases) that he only "overrides" a subset (critical) of properties of its instantiated object, i.e. the stored object only maintains and overrides part of the the properties, leaving the rest intact (i.e. according to user-specific defaults). The content of this stored object must be dynamic in my case, i.e. the "saved subset" might be different in different saved objects (records).
Last edited by mik (2016-02-26 20:52:01)
Offline
You can define stored function for each published property of your class. And use doNotStoreDefault options during serialisation. I do this in my code. Unfortantly in this case we ned to add many functions in classes.
TBaby = class
private
fName: RawUTF8;
function canStoreName: boolean;
published
property Name: RawUTF8 read fName write fName stored canStoreName;
end;
Offline
thanks, mpv!
This looks like a possible solution. Fortunately, it appears that [woDontStoreDefault] is used by default in TJSONSerializer
Offline
Why not just use a variant field, with getter and setter methods ?
The read/write methods would use a temporary TDocVariantData for customizing the serialization.
Then it would be stored as a JSON content, and you would be able to tune the serialization at runtime, just as you wish.
Offline
Thanks, ab! I will check out both approaches. My first intuitive impression, is that mpv's suggestion may be preferrable if there are only a few properties (out of many) which have to be optionally stored - the code seems to be cleaner and easier to understand with this approach. The TDocVariant method might work better in my case, where all the numerous properties are stored optionally.
Offline
@ab - I found what FPC implementation of mORMot.TPropInfo.IsStored is incompleate for FPC compiler (handle only consts). Please apply a 80 pull request to fix a problem
Offline
Has just been fixed as part of https://synopse.info/fossil/info/e58267d974
Offline
I have already checked your changes - all work as expected. Thanks!
P.S. - I deleted already merged branches from github
Last edited by mpv (2018-02-06 20:16:03)
Offline