Deleting Foreign-Key Relationships in EF4

I try to keep up with blog posts on the net which involve the entity framework, and this afternoon I came across this post where someone had been experimenting with EF4 and encountered some cases where things didn’t behave as they expected.  I started to respond in a comment to that blog but the comment starting getting awfully long.  So here’s a more complete response (which I’ll then refer to in a short comment).  Please take a moment now to go read the other post for context.  OK.  Now that you are back, here’s my response:

When you use FK-relationships (the default with EF4), there are 3 main kinds of configurations you can encounter which each have slightly different behaviors.  Let me see if I can explain so that you can understand better why the EF behaves the way it does.

  1. The first case is actually what appears in the first and last examples.  It's where the relationship is backed by a foreign key property on one of the entities, and where that foreign key is non-nullable but also not part of the primary key of the entity.  In this case, if you delete the "dependent" entity (namely the one with the FK property--typically on the many side of a 1-many relationship), then the EF knows that the entity going away means the relationship must go away as well.  If, however, you just remove the relationship (either by removing the entity from a collection on the other side or by clearing a reference navigation property or something like that), then the EF attempts to make the foreign key property null which is what will remove the relationship without removing the entity.  The problem is that since the FK property is non-nullable, when you attempt to save changes, the EF will throw an exception.  The EF will allow you to be temporarily in this state which makes it easier to write code where you want to switch an entity to be related to some other entity, but it won't allow you to save when you are in that state.  As a side note, the EF actually goes to significant lengths to support this temporary “conceptual null” so that it’s more convenient to program against entities with non-nullable foreign keys. 

    This kind of configuration is typically used to model scenarios where for a particular entity a relationship is required (you can’t have an order without a customer or something like that), and the EF's approach is to say that removing the relationship without deleting the entity is an error in your business logic.

  2. The second configuration is a simple variation on the first one where everything is the same except that the foreign-key *is* nullable.  In this case, when you remove the relationship, the foreign key property is set to null, and when you save everything works fine.  This configuration didn’t come up in the samples mentioned in the blog post, but it is something we encounter periodically.  Typically you would use something like this to model an optional relationship where an entity might have a related entity or might not—for example, I might have a salesperson for an order or maybe the order came in over the internet and there’s no salesperson. 

  3. Finally, there’s the case where the foreign-key property on one entity is also part of that entity’s primary key.  In this case the entity’s identity is fundamentally dependent on the relationship.  If you remove the relationship, then the entity must be removed.  If you change the relationship to some other entity, then you still have to remove the entity and then insert a new one because the entity with a particular primary key went away and a new entity which may be mostly the same (but which has a different primary key so it’s a different entity) has been added.  So when you have this kind of case, if you remove the relationship then the EF will remove the entity.  This scenario is typically used to model a containment sort of relationship—like the order details lines in an order.  If you remove the relationship between an order and its order detail, then the order detail line should go away.  It’s also true, by the way, that this configuration implies cascade delete behavior from the principal entity to the dependent entity (or entities).  So if you delete the order, then all the order detail entities associated with it will also be deleted.

Another variation to be aware of here in the first two cases is that you can have cascade delete behavior turned on.  If you do, then deleting the principal entity will cause the dependent entity to also be deleted.  Cascade delete does NOT, however, affect what happens when you remove the relationship.  The only case in current releases of the EF where removing the relationship will cause the dependent entity to be removed is #3 above where the primary key of the dependent includes a foreign-key to the principal.  An additional option we’d like to add for a future release of the EF is the ability to opt-in to an auto-delete orphans or cascade relationship or whatever you might want to call it behavior where case #1 above could have an additional option turned on so that it would delete the dependent rather than throwing an exception, but for now this is logic you will have to write yourself.

- Danny