EF 4.1 and “Collection was modified; enumeration operation may not execute.” exception

前端 未结 4 1586
梦如初夏
梦如初夏 2020-12-09 10:27

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 N         


        
相关标签:
4条回答
  • 2020-12-09 11:11

    I ran into the same issue this morning, in my case I had an "Employee-Manager" association defined with a circular relationship. For example:

    public class Employee 
    {
        public string Name { set; get; }
        virtual public Employee Manager { set; get; }
    
        public Employee()
        {
    
        }
    }
    

    The app was crashing with the error above when setting the Manager property. At the end it turned out to be a bad implementation of the GetHashCode() method in the Employee class. It seemed that EF was not able to spot the modified entity since the comparison among the entities was failing; thus, EF was thinking the collection had been modified.

    0 讨论(0)
  • 2020-12-09 11:18

    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:

    public class TestA // corresponds to your Employee
    {
        public int Id { get; set; }
        public TestB TestB { get; set; } // your Employer
    }
    
    public class TestB // your Employer
    {
        public TestB()
        {
            TestCs = new List<TestC>();
        }
    
        public int Id { get; set; }
        public ICollection<TestC> TestCs { get; set; } // your EmployeeRoles
    }
    
    public class TestC // your EmployeeRole
    {
        public int Id { get; set; }
        public TestA TestA { get; set; } // your Employee
    }
    

    These are three entities with cyclic relationships:

    TestA -> TestB -> TestC -> TestA
    

    If I use now a corresponding code with the same structure than yours I get the same exception:

    var testA = new TestA();
    var testB = new TestB();
    var testC = new TestC();
    
    context.TestAs.Add(testA);
    
    testA.TestB = testB;
    testB.TestCs.Add(testC);
    testC.TestA = testA;
    
    context.ChangeTracker.DetectChanges();
    

    Note that I have used DetectChanges instead of SaveChanges because the stack trace in the exception makes clear that actually DetectChanges causes the exception (which is called internally by SaveChanges). I also found that calling SaveChanges 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 the ObjectStateManager. I could verify this by replacing ICollection<TestC> TestCs with a single reference by TestC TestC in the TestB 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 call SaveChanges:

    var testA = new TestA();
    var testB = new TestB();
    var testC = new TestC();
    
    testA.TestB = testB;
    testB.TestCs.Add(testC);
    testC.TestA = testA;
    
    context.TestAs.Add(testA);
    
    context.ChangeTracker.DetectChanges();
    

    EF will add the whole related object graph to the context. This code succeeds (also using SaveChanges instead of DetectChanges).

    Or your example:

    using (DbContext db = DbContext.GetNewDbContext()){
        Employee creator = new Employee("Bob");
        Employer employer = new Employer("employer", creator);
    
        db.Employees.Add(creator);
        db.SaveChanges();
    }
    

    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:

    var testA = context.TestAs.Find(1); // assuming there is already one in the DB
    var testB = new TestB();
    var testC = new TestC();
    
    testA.TestB = testB;
    testB.TestCs.Add(testC);
    testC.TestA = testA;
    
    context.SaveChanges(); // or DetectChanges, it doesn't matter
    

    So, I want to add relationships with new entities to the existing entity. A fix to this problem seems to be less obvious.

    0 讨论(0)
  • 2020-12-09 11:23

    I've just recently come across this problem myself. What I found is that EF hates indirect circular references. In your case it seems Employer should own the relationship to employee. So take out the reference back to employer from the employee class.

    0 讨论(0)
  • 2020-12-09 11:25

    I had the same problem. It looks like a bug in EF.

    I changed my code to explicitly add the entity to EF context before setting it to any other entity property.

    0 讨论(0)
提交回复
热议问题