How can I clone an entity?

I need to clone an entity and all its dependencies. That is some case could have several TList<>


The cloned Entity should have the ID blank since it will be saved as a New Record on the same table.

Is there support for that ?

Thanks

Eduardo


I have found this generic solution for cloning:


http://www.yanniel.info/2012/02/deep-copy-clone-object-delphi.html

According the author capable of complex cases. However I have the problem that the ID is copied, including in the TLists. I needed a way to make them all empty.

Looking onto this, remembered that you have the serializer and deserializer in Aurelius already, 


Can I use this solution to clone an entity? (seems that i can) 

How can I force the Lazy loading to be loaded automatically for serialization? For cloning it needs to be everything in memory.

Can I traverse the JSon generated by the serializer and change to 0 all the $id and FId that I could find? in order to have a "new" record to be saved?

There are two undocumented methods in TMappingExplorer that you can use to help you with this: CopyFieldValues and SetIdValue. You can yse CopyFieldValues to clone the object, and then SetIdValue (passing NULL for example) to clear the id value. Note that since those are undocumented methods, they are subject to change in future versions.

CopyFieldValues will handle proxies (not sure why you need to force proxy to load)?

For example, if I have an invoice that I need to clone:


TInvoice
Items: TList<TInvoiceItem>
end

If I clone the TInvoice, the Items needs to be cloned also. They are part of the same "document" besides are in separated tables.

The problem i see is not in all cases this is correct. for example:

TProduct
FUnity: TUnity
FTax: TTax

In this case clonning TProduct should keep the same entities associated, 
Item: Banana  Unity: KG

Cloning that will keep the same unity, however cloning Invoice, the Invoice Items needs to be cloned also.

I was thinking if it is possible to add a new tag to refer to this cloning. If can be or not cloned. Then I can go thru the tags and see which association should have its ID GUID nulled (that will end up as a new record)

In this case you have to do something manually indeed. It depends on your business logic.

Nevertheless, you can still use CopyFieldValues to do the basic then iterate through the associations that need to be cloned as well and clone/replace instances there manually.

Wagner, 


I am using CopyFieldValue for my cloning process and it works fine.

However I have a question. SInce now I need that one of my Associations needs to be kept.

This is what i need:

- first time I make a complete clone of the entity and save it with new id. Both entities are now pointing to same associations.
- at some point i need to update this clone and change ONE of the associations, that is fine, I load it and then change the association to a new entity and save it.
- however all these clones keep a relation with its originator. Therefore every time the original entity is updated I need to update the clone, but not that association that was changed.

so I have this associations:
    [Association([], [])]
    [JoinColumn('AJIDITEMPRECO', [])]
    FItemPreco: TEntityItemPreco;

before running the CopyFieldValues what I should do to protect this FItemPreco to be updated? 

I was thinking on have 2 methods declared on the class of the entity:

ProtectData  : save internally those fields that should  not be copied
RestoreData : restore from the internal copy 

What I understand is that, in this case that has no proxy, if there is a value for the association means that the object is created in memory. So protecting the association means keeping a copy of the pointer to the object in memory. 

The CopyFieldValues will then copy from the source the pointer to the entity to the association, overriding the value. Since CopyFieldValues resolves the proxies and any other need to load data and create in memory objects. So it is a pointer that is getting copied.

After the copy I can restore the pointer, since the object remains in memory. 

Please, I need to validate if this is correct, since creating an example for testing will be too extensive.

It looks like it will work.

Wagner


Going further on this cloning thing.

I made to work a simple cloning system on my software that is not "deep cloning".

It means I am only using the CopyFieldValues. But in one entity this is behaving very weird.

Snip:

TEntityTabelaPreco = class
    [ManyValuedAssociation([TAssociationProp.Lazy], [])] 
[ForeignJoinColumn('FJIDTPRECOITEM', [])] 
  FItems: Proxy<TList<TEntityTabelaPrecoItem>>;

it is a regular Master detail entities. It is working normally.

However I need to clone the master, I make a clone of the master for each company on my software, and save it with new ID (guid)

Since TTabPreco have many TTabPrecoItem it should keep the link to the corresponding Item. No cloning of the item.

This is needed because each company has a number (dominio) that is always used to filter its own data on the database. I need that the company can see all TTabPreco available to be used, but all of them are mantained by the HQ. This is why there is no "local" copy of the items. When the HQ change the price of the item, all tables get automatically updated, since are all connected to that.

But it does not behave like that. My database is getting imported, by a csv reader that generates the masters and the details. The masters are getting generated correctly, 20 companies x 6 master TTabPreco.

The Items are also getting generated, here the problem happens: the when I save the master entity Aurelius start updating the constraint:

CONSTRAINT "FK_TABPRECOITEM_TABPRECO_FJIDTPRECOITEM" FOREIGN KEY ("FJIDTPRECOITEM") REFERENCES "TABPRECO" ("ID")
   ON UPDATE NO ACTION ON DELETE NO ACTION

And for each master it saves something that is not what I expected. I cannot tell you what is the logic yet. But I was expecting that even making a clone of each master, it would maintain the original correspondence.

Seems that this is not the case, somehow this cloning is making it change its internal mapping for who is the owner. 

I need your help on how should I proceed on this case.

What I do with each entity is using my model class to make the TobjectManager operations, for example:

procedure TNaharModel.SaveOrUpdate(Entity: TObject);
begin
  CheckEntityInitialized(Entity as TNaharEntity);
  (Entity as TNaharEntity).Action(eaBeforeSave);
  OManager.SaveOrUpdate(Entity);
  ProcessUpdate(Entity as TNaharEntity);
end;

I test if my entity is already initialized, is not I do it, this is when the entity knows the mapping for cloning, commonly the cloning process is a way to have the HQ data replicated to the other companies.

THen I call the SaveOrUpdate from the Object Manager.

After that I Clone the Entity, use the SetIdValue(null) to get the ID = null (guid) and then Save.

I believe that because there are items on FItems it updates the constraint. I see it doing for each master, update the very same items constraints, but it is hard to see what is doing. The end result is not the originals ID.

How can I change this behavior on Aurelius?

If you add an item to a collection, Aurelius will set that item belonging to that collection on a Flush.

I think the problem here is design: you are desigining a one-to-many association, but you actually want a many-to-many (two different price tables can reference the same five differente price items). You should redesign your list so that TEntityTabelaPreco contains a list of references to TPrecoItem, not the TPrecoItem object themselves.

Is possible to use Evict on the "old" document and then combine the items in the the same way? Then set all the IDs to 0 (zero) and re-save the same (now evicted and with all IDs to zero) document again with the manager?

Would this work?

I'm stuck on the same problem - clone a document with items.. If there is some example code, I would be great.

This is a 9-year old thread. I don't know exactly what are you struggling with, what do you want to achieve, exactly?
With modern Aurelius you an easily use Merge/Replicate to "clone" an entity into an object manager.
I will close this topic and if you have a specific question please describe it from scratch in another topic, thank you.

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