Update: Danny was quick to respond with a great explanation about how EF4 deals with object-graphs and deletes.
I’ve been getting up to speed on Entity Framework V4 (EF4) lately, for use in a WPF application that I’m working on. One of the issues I’m coming up against is removal of objects in one-to-many and many-to-many relationships. It’s still not clear to me why things work the way they do in EF4, but I’ll lay out a few scenarios and talk about what works and what doesn’t work and hope that someone can later comment as to why things may not be working as I would expect.
Scenario 1: A many-to-many relationship where the join table has its own primary key (an identity column in this case):
Given the following code, I’d expect to be able to remove an object from the Employee.ProjectAssignments collection and see that object removed from the database when I call SaveChanges on my ObjectContext:
using (var context = new EFTestDBEntities()){
var employeeGraph = (from e in context.Employees.Include("ProjectAssignments").Include("ProjectAssignments.Project") where e.ID == employeeID select e).FirstOrDefault();
if (employeeGraph != null){
var assignment = employeeGraph.ProjectAssignments.First();
employeeGraph.ProjectAssignments.Remove(assignment);
context.SaveChanges();
}
}
When I hit the “SaveChanges()” call, I get the following exception:
System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
This strikes me as strange – I assume that I’ve indicated that the employee is no longer related to this project-assignment, so the object is essentially “worthless” and I’d expect it to be removed.
In order to successfully remove this project-assignment, I had to do the following:
using (var context = new EFTestDBEntities()){
var employeeGraph = (from e in context.Employees.Include("ProjectAssignments").Include("ProjectAssignments.Project") where e.ID == employeeID select e).FirstOrDefault();
if (employeeGraph != null){
ProjectAssignment assignment = employeeGraph.ProjectAssignments.First();
context.ProjectAssignments.DeleteObject(assignment);
context.SaveChanges();
Assert.AreEqual(0, employeeGraph.ProjectAssignments.Count);
}
}
Okay, so if I “manually” delete the object by the explicit call to DeleteObject, things work. So far so good, I can work with this.
Scenario 2: A many-to-many relationship where the join table has a primary key that is composed by foreign-keys to the tables on either side of the relationship:
In this case, the code I’d expect to work, actually worked and allowed me to delete a class-schedule from a teacher:
using (var context = new EFTestDBEntities()){
var teacherGraph = (from t in context.Teachers.Include("ClassSchedules").Include("ClassSchedules.Class") where t.ID == teacherID select t).FirstOrDefault();
if(teacherGraph != null){
var classSchedule = teacherGraph.ClassSchedules.First();
teacherGraph.ClassSchedules.Remove(classSchedule);
context.SaveChanges();
}
}
Why does EF4 make a distinction in behavior between these two scenarios that are essentially the same operation yet one object-graph allows me to write what I naturally want to write (scenario 2), but not the other?
Scenario 3: A one-to-many relationship:
Once again, I tried to write the code in a way I’d expect to naturally work:
using (var context = new EFTestDBEntities()){
var orderGraph = (from o in context.Orders.Include("OrderDetails") where o.ID == orderID select o).FirstOrDefault();
if(orderGraph != null){
var detail = orderGraph.OrderDetails.First();
orderGraph.OrderDetails.Remove(detail);
context.SaveChanges();
}
}
And once again, I got the following exception:
System.InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
As with scenario 1, I had to write the following code to successfully remove the OrderDetail record:
using (var context = new EFTestDBEntities()){
var orderGraph = (from o in context.Orders.Include("OrderDetails") where o.ID == orderID select o).FirstOrDefault();
if (orderGraph != null){
var detail = orderGraph.OrderDetails.First();
context.OrderDetails.DeleteObject(detail);
context.SaveChanges();
}
}
Alright, so I have managed to work around some of the quirks with EF4, but I really don’t understand why removal of objects in the object-graph don’t cause a proper remove/delete in some cases, yet work just as I expect in others.