Saving modified entity incl related entities without refetch

Posts   
 
    
Rosacek
User
Posts: 155
Joined: 18-Mar-2012
# Posted on: 03-Jan-2019 10:30:25   

Adapter, LLBLGEN Pro v.4.2

Hi, I have following issue when saving modified entity incl related entities without refetch

adapter.SaveEntity(CurrentEntity, False, parUpdateRestriction, True)

Say I have USER entity having ROLES USER entity has validator checking before saving that user has assigned at least one active role.

When saving new USER entity, it works fine. Firstly user is validated (if having at least 1 active role - this related entity is not saved yet) Then USER is saved Then its ROLES are validated and saved

Now I want to modify user role AND ALSO modify USER property like eg. password

Now the order of saving is changed, first related ROLES entities then USER entity:

First USER.ROLES are validated and saved (and not refetched) Then USER is validated, but exception is raised when validating if USER entity has at least one active role by LINQ statement ...User2Roles.Where(Function(c) c.IsDeletedOn Is Nothing).Count = 0

because Role(1..n) is OutOfSync, therefore C.IsDeleted is OutOfSync, because any ROLE was saved before USER validation and not refetched as SaveEntity refetch parameter was false

I know that simple solution is to change SaveEntity refetch parametr to TRUE, but this is just workaround as do not need refetch.

There is clear reason why new USER entity is saved first, then its ROLES. Why this saving sequence is changed when saving modified USER entity, first saving related entities and then USER entity?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 03-Jan-2019 15:49:18   

The order in which they're saved in the update situation is simply the order in which they're appearing in the topological sort result and in this case that's the reverse of the visit (topological sorting is doing a DFS and then reverse the result) order. There's no necessity to persist one before the other here (at least not for PK-FK ordering) so the entities are persisted in the order of the topological sorter. In this case this works against you as you have an extra constraint for dependency, which isn't taken into account.

You can use: https://www.llblgen.com/Documentation/5.5/ReferenceManuals/LLBLGenProRTF/html/18E1127C.htm

(so set EntityBase2.MarkSavedEntitiesAsFetched to true), which will avoid the refetch as it will mark the entity as 'fetched' after the save, so no error will be given in your situation.

Frans Bouma | Lead developer LLBLGen Pro
Rosacek
User
Posts: 155
Joined: 18-Mar-2012
# Posted on: 03-Jan-2019 17:08:39   

Thanks Otis for quick support simple_smile

I wanted to avoid setting EntityBase2.MarkSavedEntitiesAsFetched = True as it may cause some headache in the future.

I found another workaround, set for each related entity IEntity2.Fields as Fetched in top level entity validator

Back to my original question. I expected that when saving graph, then first all entities in graph are validated and if no error, then everything is saved. Seems effective way. And when SaveEntity refetch param=False then no side effects.

Current logic in your engine is all related entities are validated and saved one-by-one within transaction. Then if top level entity validation failed then transaction is rolled back. Seems to me more expensive logic as after several/many db INSERTS/UPDATES everything has to be rolled back. Just my two cents.

EDIT: current logic is even more complicated. All INSERTS of related entities happen first (validate + save entity) Then validation of top level entity + save Then validation+save of all UPDATED related entities.

Workaround by temporary setting all OutOfSync related entities to Fetched works fine to me in top level entity validator.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 04-Jan-2019 03:02:55   

Rosacek wrote:

I found another workaround, set for each related entity IEntity2.Fields as Fetched in top level entity validator

Ok. Just be careful simple_smile Remember that with the EntityBase2.MarkSavedEntitiesAsFetched approach you can set it on/off on your code.

Rosacek wrote:

Current logic in your engine is all related entities are validated and saved one-by-one within transaction. Then if top level entity validation failed then transaction is rolled back. Seems to me more expensive logic as after several/many db INSERTS/UPDATES everything has to be rolled back. Just my two cents.

I think that the opposite can be a problem as well for a person that need the validation of the nodes first and then the root entity.

Rosacek wrote:

EDIT: current logic is even more complicated. All INSERTS of related entities happen first (validate + save entity) Then validation of top level entity + save Then validation+save of all UPDATED related entities.

If you use UnitOrWork, you can specify the order in wich you want to perform insert/update/delete actions.

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39590
Joined: 17-Aug-2003
# Posted on: 04-Jan-2019 10:50:01   

Some more info:

Rosacek wrote:

Back to my original question. I expected that when saving graph, then first all entities in graph are validated and if no error, then everything is saved. Seems effective way. And when SaveEntity refetch param=False then no side effects.

Current logic in your engine is all related entities are validated and saved one-by-one within transaction. Then if top level entity validation failed then transaction is rolled back. Seems to me more expensive logic as after several/many db INSERTS/UPDATES everything has to be rolled back. Just my two cents.

That's always a risk, e.g. an FK field isn't set after the PK side has been saved (and it's new), validating whether that field is set then makes things go wrong if validation is done before the save action.

But indeed in the situation where you could avoid persisting a graph up front with validation that's not depending on DB values, then it could avoid the whole query generation etc. but are these situations common? In any case, for the caller the situation is the same: the graph has to be corrected and re-persisted.

EDIT: current logic is even more complicated. All INSERTS of related entities happen first (validate + save entity) Then validation of top level entity + save Then validation+save of all UPDATED related entities.

As David said, if you really want to change this order, a UoW is required. The order in which we persist entities is the one where things always work: inserts first, updates later, pk first, fk later.

So effectively what you're after is a graph validation where the whole graph is validated for persistence by user supplied code, and if validation fails, the persistence is aborted, otherwise continued? This would mean all entities in the queues (insert and update) are then validated before the queues are persisted (as both queues are known up front). In a way you can do this with a Unit of Work, but it requires some custom code (you could derive a class from UnitOfWork2 to implement this) -> In your derived class from UnitOfWork2, you add an override for Commit(). In there, you first call ConstructSaveProcessQueues(), then call GetInsertQueue and then GetUpdateQueue, and validate all entities in those 2 lists. If one fails, you can simply exit the method, return 0, throw an exception etc., if not, you can call the base' commit method.

Frans Bouma | Lead developer LLBLGen Pro