EF 4.1和“集合已被修改;枚举操作可能无法执行”。例外

时间:2011-09-24 01:51:49

标签: c# entity-framework-4.1

过去两天这让我疯了。 我有3个非常基本的类(好了,可读性降低了)

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;
    }
}

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;
    }
}

看起来很简单。没有为DbContext中的那些类做任何特定配置。

但是,当我运行以下代码时

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.
}

抛出以下异常:

  

收藏被修改;枚举操作可能无法执行。

堆栈追踪:

  

在System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource   资源)   System.Collections.Generic.List 1.Enumerator.MoveNextRare() at System.Collections.Generic.List 1.Enumerator.MoveNext()at   System.Data.Objects.ObjectStateManager.PerformAdd(IList的1 entries)
at System.Data.Objects.ObjectStateManager.AlignChangesInRelationships(IList
1   System.Data.Objects.ObjectStateManager.DetectChanges()处的条目)   在System.Data.Objects.ObjectContext.DetectChanges()at   System.Data.Entity.Internal.InternalContext.DetectChanges(布尔   强迫)   System.Data.Entity.Internal.Linq.InternalSet 1.ActOnSet(Action action, EntityState newState, Object entity, String methodName) at System.Data.Entity.Internal.Linq.InternalSet 1.Add(Object entity)
  在System.Data.Entity.DbSet`1.Add(TEntity entity)

任何人都知道发生了什么,也许是如何解决这个问题? 谢谢!

4 个答案:

答案 0 :(得分:16)

对我来说,这看起来像是Entity Framework中的一个错误。我已经将你的例子简化为一个更简单的例子,但具有相同的结构:

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
}

这是具有循环关系的三个实体:

TestA -> TestB -> TestC -> TestA

如果我现在使用与你的结构相同的相应代码,我会得到同样的例外:

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();

请注意,我使用了DetectChanges而不是SaveChanges,因为异常中的堆栈跟踪清楚地表明实际DetectChanges导致异常(由SaveChanges内部调用)。我还发现两次调用SaveChanges不是问题。这里的问题只是在整个对象图完成之前向上下文添加“早期”。

修改后的集合(例外情况是抱怨)是模型中的TestB.TestCs集合。它似乎是ObjectStateManager中的条目集合。我可以通过ICollection<TestC> TestCsTestC TestC中的TestB替换SaveChanges来验证这一点。这样,模型根本不包含任何集合,但它仍然会针对修改后的集合抛出相同的异常。 (DetectChanges虽然有三个单引用但会失败,因为EF不知道由于循环而以哪种顺序保存实体。但这是另一个问题。)

我认为这是一个错误,EF更改检测(Add)似乎修改了它自己的内部集合,它正在迭代。

现在,解决此问题的方法很简单:在调用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(); 个实体作为上下文的最后一步:

SaveChanges

EF会将整个相关对象图添加到上下文中。此代码成功(也使用DetectChanges代替using (DbContext db = DbContext.GetNewDbContext()){ Employee creator = new Employee("Bob"); Employer employer = new Employer("employer", creator); db.Employees.Add(creator); db.SaveChanges(); } )。

或者你的例子:

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

修改

这是同样的例外:Entity Framework throws "Collection was modified" when using observable collection。遵循该示例中的代码情况类似:向上下文添加实体,然后更改/添加与该实体的关系。

<强> EDIT2

有趣的是,这引发了同样的例外:

{{1}}

因此,我想将新实体的关系添加到现有实体中。解决这个问题似乎不太明显。

答案 1 :(得分:2)

我最近才遇到这个问题。我发现EF讨厌间接循环引用。在您的情况下,似乎雇主应该拥有与员工的关系。因此,请从员工班中将参考资料退回给雇主。

答案 2 :(得分:2)

我遇到了同样的问题。它看起来像EF中的一个错误。

我将代码更改为在将实体设置为任何其他实体属性之前将实体显式添加到EF上下文。

答案 3 :(得分:0)

我今天早上遇到了同样的问题,在我的情况下,我有一个以循环关系定义的“员工 - 经理”协会。例如:

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

    public Employee()
    {

    }
}

在设置Manager属性时,应用程序因上述错误而崩溃。最后,结果是 Employee类中GetHashCode() 方法的错误实现。由于实体之间的比较失败,EF似乎无法发现修改后的实体;因此,EF认为该集合已被修改。