views:

550

answers:

1

I have run into an interesting problem with Entity Framework and based on the code I had to use to tackle it I suspect my solution is less than ideal. I have a 1-to-Many relationship between Table A and Table B where entities in TableB have a reference to TableA. I have a scenario where I want to simultaneously delete all children of a row in TableA and I thought this could be achieve by simply clearing the collection:

Entity.Children.Clear()

Unfortunately, when I attempted to save changes this produced as a Foreign Key violation.

A relationship is being added or deleted from an AssociationSet 'FK_EntityB_EntityA'. With cardinality constraints, a corresponding 'EntityB' must also be added or deleted.

The solution I came up with was to manually delete object via the entity context's DeleteObject(), but I just know this logic I am using has got to be wrong.

while (collection.Any())
    Entities.DeleteObject(collection.First());

For one, the fact that I had to use a Where() loop seems far less than ideal, but I suppose that's purely a semantic assessment on my part. In any case, is there something wrong with how I am doing this, or is there perhaps a better way to clear a child entity collection of an entity such that Entity Framework properly calls a data store delete on all of the removed objects?

+2  A: 

Clear() removes the reference to the entity, not the entity itself.

If you intend this to be always the same operation, you could handle AssociationChanged:

Entity.Children.AssociationChanged += 
    new CollectionChangeEventHandler(EntityChildrenChanged);
Entity.Children.Clear();            

    private void EntityChildrenChanged(object sender,
        CollectionChangeEventArgs e)
    {
        // Check for a related reference being removed. 
        if (e.Action == CollectionChangeAction.Remove)
        {
            Context.DeleteObject(e.Element);
        }
    }

You can build this in to your entity using a partial class.

Craig Stuntz
This looks perfect. From a partial class is there any direct (or indirect) route to the Context?
Nathan Taylor
yeah there is a indirect route: http://blogs.msdn.com/alexj/archive/2009/06/08/tip-24-how-to-get-the-objectcontext-from-an-entity.aspx
Alex James
It almost seems that I'm better off just building the while() loop deletion into my repository so I can reuse it easily. I won't have to worry about the caveats of resolving the context and it's suitably flexible. Still, I haven't had to write a while() loop in C# for quite some time, it feels almost wrong.
Nathan Taylor
The changed event is completely generic and can be used in a repository, also (indeed, that's how I use it). So feel free to use it if you don't like `while`. :) Seriously, it has the advantage that it still works even if someone forgets to call certain repository code; the event always fires.
Craig Stuntz
From within my repository with access to the Context how can I attach the AssociationChanged event to all of the entities in the set? It seems I wouldn't want to loop over them all because that would cause a database lookup just to get the object reference.
Nathan Taylor
That really depends upon your repository design. The way we do it is that there is a specific method that a client of the repository must call to get a mutable object (MergeOption set appropriately, will save changes eventually, etc.). When an object is returned from this route, the repository will assign the event handler. This works well for us with our specific use case, but might not work for you, depending upon what you are doing. Alex's tab will allow you to do this completely within the entity object. In short, I think you have to pick a method which works with your specific code.
Craig Stuntz