Reputation: 13083
What is the right way to delete all of the collection items of an EF entity? In the code below, DocumentItems is the collection of related document items for a document. This code proceedes on Clear() but fails on SaveChanges() because related items are connected to their document via FK and FK is mandatory. So I guess they somehow remain floating up in the air without a foreign key after Clear().
Do I solve this with a foreach loop over the collection calling Remove() on each item or is there another way?
// remove existing document items to prepare for refreshing them
existing.DocumentItems.Clear();
// adds new Document Items
PrepareInvoice(existing, collection);
_repository.SaveChanges();
Upvotes: 15
Views: 32381
Reputation: 1912
Just to answer to Nix comment to the answer,
it seems to me that the EntityCollection.Remove()
method only marks for deletion the relationships and not the entities, just as the EntityCollection.Clear()
method does.
I know that documentation says that also the entity will be marked for deletion but in my test I've got the behavior I described (anyone can explain me why?).
So, if you have a one to many foreign key constraint in your conceptual model, you cannot save the changes to the context in the persistence store.
The only way I found (since I don't want to CascadeDelete
) is looping through the children and invoke context.DeleteObject
on each of them, thus removing the entity and the associated relationship.
Upvotes: 0
Reputation: 58522
Clear just removes the reference but doesn't delete the entity.
In your situation
existing.DocumentItems.Clear();
All DocumentItems
in the EntitySet
will get cleared but you will have to Remove/Delete the actual DocumentItem
or the commit with fail, just the same as it would if you tried to delete it in the database.
You need to loop through detach any references, and then delete the entity you wish to remove (unless its nullable
and in your situation, it is not)
Alternatively, I have seen implementations that use clear, and an AssociationChangedHandler
to automatically delete the old object. Basically, if the change is a "delete/remove" it calls DeleteObject()
on the orphaned object.
Upvotes: 13
Reputation: 6044
Trick: When setting up the relationship between Parent and Child, you'll HAVE TO create a "composite" key on the child. This way, when you tell the Parent to delete 1 or all of its children, the related records will actually be deleted from the database.
To configure composite key using Fluent API:
modelBuilder.Entity<Child>.HasKey(t => new { t.ParentId, t.ChildId });
Then, to delete the related children:
var parent = _context.Parents.SingleOrDefault(p => p.ParentId == parentId);
var childToRemove = parent.Children.First(); // Change the logic
parent.Children.Remove(childToRemove);
// or, you can delete all children
// parent.Children.Clear();
_context.SaveChanges();
Done!
Upvotes: 2
Reputation: 858
This is one way of deleting the items in the collection.
VB
TEntityCollection.ToList().ForEach(Sub(o) ctx.DeleteObject(o))
C#
TEntityCollection.ToList().ForEach(x => ctx.DeleteObject(x))
Then you need to call
ctx.SaveChanges()
Upvotes: 13
Reputation: 664
Yeah, a year old, but on a minor note... since DeleteObject takes one parameter, which is the same type as the argument for the lambda expression, you can just use:
entityCollection.ToList().ForEach(ctx.DeleteObject);
I am not sure if VB supports a similar syntax, though. Anyone?
Upvotes: 1