This has been driving me nuts for the last 2 days.
I have 3 pretty basic classes (well, reduced for readability)
public class Employee
{
public string Name { set; get; }
virtual public Employer Employer { set; get; }
public Employee(string name)
{
this.Name = name;
}
}
,
// this basically ties Employee and his role in a company.
public class EmployeeRole{
public int Id { set; get; }
virtual public Employee Employee { set; get; }
public string Role { set; get; }
public EmployeeRole(Employee employee, string role){
this.Employee = employee;
this.Role = role;
}
}
and
public class Employer{
public string Name { set; get; }
List<EmployeeRole> employees = new List<EmployeeRole>();
virtual public List<EmployeeRole> Employees { get { return this.employees; } }
public Employer(string name, Employee creator){
this.Name = name;
this.Employees.Add(new EmployeeRole(creator, "Creator"));
creator.Employer = this;
}
}
Seems pretty simple. Not doing any specific configuration for those classes in DbContext.
But, when I run following code
using (DbContext db = DbContext.GetNewDbContext()){
Employee creator = new Employee("Bob");
db.Employees.Add(creator);
db.SaveChanges();
Employer employer = new Employer("employer", creator);
db.Employers.Add(employer);
db.SaveChanges();
// I know I can call SaveChanges once (and it actually works in this case),
// but I want to make sure this would work with saved entities.
}
It throws following exception:
Collection was modified; enumeration operation may not execute.
Stack trace:
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource
resource) at
System.Collections.Generic.List1.Enumerator.MoveNextRare() at
1.Enumerator.MoveNext() at
System.Collections.Generic.List
System.Data.Objects.ObjectStateManager.PerformAdd(IList1 entries)
1
at
System.Data.Objects.ObjectStateManager.AlignChangesInRelationships(IList
entries) at System.Data.Objects.ObjectStateManager.DetectChanges()
at System.Data.Objects.ObjectContext.DetectChanges() at
System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean
force) at
System.Data.Entity.Internal.Linq.InternalSet1.ActOnSet(Action action,
1.Add(Object entity)
EntityState newState, Object entity, String methodName) at
System.Data.Entity.Internal.Linq.InternalSet
at System.Data.Entity.DbSet`1.Add(TEntity entity)
Anyone has an idea what's going on and maybe how to fix it ?
Thanks !
Best Answer
For me this looks like a bug in Entity Framework. I've cooked down your example to a simpler one but with the same structure:
These are three entities with cyclic relationships:
If I use now a corresponding code with the same structure than yours I get the same exception:
Note that I have used
DetectChanges
instead ofSaveChanges
because the stack trace in the exception makes clear that actuallyDetectChanges
causes the exception (which is called internally bySaveChanges
). I also found that callingSaveChanges
twice is not the problem. The problem here is only the "early" adding to the context before the whole object graph is completed.The collection which was modified (as the exception is complaining about) is not the
TestB.TestCs
collection in the model. It seems to be a collection of entries in theObjectStateManager
. I could verify this by replacingICollection<TestC> TestCs
with a single reference byTestC TestC
in theTestB
class. This way the model doesn't contain any collection at all but it still throws the same exception about a modified collection. (SaveChanges
will fail though with three single references because EF doesn't know in which order to save the entities due to the cycle. But that is another problem.)I would consider it as a bug that EF change detection (
DetectChanges
) seems to modify its own internal collection it is just iterating through.Now, the fix to this problem is easy: Just
Add
entities to the context as your last step before you callSaveChanges
:EF will add the whole related object graph to the context. This code succeeds (also using
SaveChanges
instead ofDetectChanges
).Or your example:
Edit
This was the same exception: Entity Framework throws "Collection was modified" when using observable collection. Following the code in that example the situation was similar: Adding an entity to the context and then afterwards changing/adding relationships to that entity.
Edit2
Interestingly this throws the same exception:
So, I want to add relationships with new entities to the existing entity. A fix to this problem seems to be less obvious.