UnitOfWork.AddToCollection order not being respected?

Posts   
 
    
ddp74
User
Posts: 4
Joined: 04-Jan-2024
# Posted on: 13-May-2024 17:07:33   

I had a really weird issue today on a production environment that's been running fine for months. I've got the following code:

UnitOfWork2 work = new UnitOfWork2();
work.AddCollectionForSave(recipeUoms);
work.AddCollectionForSave(subRecipes);
work.Commit(adapter);

RecipeUoms is the PK table with the columns

  • Id uniqueidentifier PK

  • RecipeId int PK

  • VersionId int PK

SubRecipes has the columns:

  • SubRecipeId int PK

  • ChildRecipeId int FK

  • ChildVersionId int FK

  • UomId uniqueidentifier FK

The mapping is:

  • RecipeUom.Id PK to SubRecipe.UomId FK

  • RecipeUom.RecipeId PK to SubRecipe.ChildRecipeId FK

  • RecipeUom.VersionId PK to SubRecipe.ChildVersionId FK

Therefor to save SubRecipe a RecipeUom record must exist first. However, using the above code and inspecting the SQL DQE output, the INSERTS for the SubRecipes are attempted first. So instead of saving the UOMs that the SubRecipes depend on, it's saving the SubRecipes and then ending up with a FK constraint error.

Calling UnitOfWork.Commit after adding the UOMs and then adding the SubRecipes and committing again works fine.

Additionally, if instead of doing AddCollectionToSave I iterate through each record and add it individually, it also works as expected and saves the UOMs first:

foreach (RecipeUomEntity uom in recipeUoms)
{
    work.AddForSave(uom);
}

work.AddCollectionForSave(subRecipes);

I checked the defaults for AddForSave and recurse is true, but when the objects are created they are created independently and shouldn't really be aware of each other.

Is there any reason why UnitOfWork2 wouldn't respect the order in which they're added when using AddCollectionForSave?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39712
Joined: 17-Aug-2003
# Posted on: 14-May-2024 07:49:23   

Which version of llblgen pro are you using?

Do the subRecipes reference the recipeUoms? If not, the runtime doesn't know which ones to insert first, it'll traverse the graph and the subRecipe entities will appear to be alone. (as it can depend on an existing recipeUom in the database).

Frans Bouma | Lead developer LLBLGen Pro
ddp74
User
Posts: 4
Joined: 04-Jan-2024
# Posted on: 14-May-2024 09:17:40   

Apologies! I'm using LLBLGen 5.9. The SubRecipe entity does reference the UOM, yes. They are connected in the database via a relationship as defined above and can be accessed via the SubRecipeEntity.Uom property. Everything looks setup correctly in the designed in this regard.

However, when constructing the entities to save I'm not assigning the UOM to the SubRecipeEntity and I'm not adding the SubRecipes to the RecipeUomEntity.SubRecipes collection. They are just two independent EntityCollections and I would have expected it to save the UOMs first and then the SubRecipes as per the ordering in code.

If that's not expected behaviour, then is there a way to force it?

Walaa avatar
Walaa
Support Team
Posts: 14975
Joined: 21-Aug-2005
# Posted on: 15-May-2024 03:52:51   

If that's not expected behaviour, then is there a way to force it?

Yes, follow the first command with a Commit, as you have stated.

ddp74
User
Posts: 4
Joined: 04-Jan-2024
# Posted on: 15-May-2024 08:34:51   

Well - yes - I know how to force it that way. I meant is there a way in code to tell it respect the ordering without me now having to go through every instance where we do saves like this and add a hundred uow.Commit lines just in case this happens again?

Or is there a way I can inspect/override the graph traversal and see why it's picking the wrong order in this instance?

And, to be clear, even though the SubRecipe entities are technically dependent on the UOMs in this instance, there's no guaranteeing whether or not it decides to save them in a different order than stated in code and that's normal behaviour?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39712
Joined: 17-Aug-2003
# Posted on: 15-May-2024 09:10:57   

ddp74 wrote:

Well - yes - I know how to force it that way. I meant is there a way in code to tell it respect the ordering without me now having to go through every instance where we do saves like this and add a hundred uow.Commit lines just in case this happens again?

Or is there a way I can inspect/override the graph traversal and see why it's picking the wrong order in this instance?

And, to be clear, even though the SubRecipe entities are technically dependent on the UOMs in this instance, there's no guaranteeing whether or not it decides to save them in a different order than stated in code and that's normal behaviour?

If you don't use batching, the system will traverse the entities recursively and based on each entity and the reachable entities in the graph it's part of, will produce a queue for persistance in the right order. If you do use batching, the entities will grouped together in batches which are scheduled in the order that is derived from the relationship direction (in your case it's going to persist subrecipes after uoms).

If you add the entities in a certain order and not specify any recurse action, and your code uses the default, which is false so there's no graph traversal/recurse happening, they're persisted in that order, so recipeUOMs first, then SubRecipes.

It's indeed weird you run into this. If you after this code:

UnitOfWork2 work = new UnitOfWork2();
work.AddCollectionForSave(recipeUoms);
work.AddCollectionForSave(subRecipes);

do:

work.ConstructSaveProcessQueues();
var insertQueue = work.GetInsertQueue();

You should see the elements in the order they'll be inserted. As adding them individually does work (which does use recurse) my suspicion is that the subrecipes do have a reference to a recipeUOM. You can check the queues for both actions using the 2 lines above.

If you can reproduce this with a few entities every time, we'll see if we can reproduce it with the same setup. (tho we need to know which code creates the entities so we know they don't have references to each other which are utilized in a traversal over the graphs).

It is odd tho that your code worked fine for some time and now all of a sudden fails.

Frans Bouma | Lead developer LLBLGen Pro