Для меня это выглядит как ошибка в 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> TestCs
единственной ссылкой на TestC TestC
в классе TestB
. Таким образом, модель вообще не содержит никакой коллекции, но все равно выдает то же исключение для модифицированной коллекции. (SaveChanges
потерпит неудачу, хотя с тремя одиночными ссылками, потому что EF не знает, в каком порядке сохранять объекты из-за цикла. Но это другая проблема.)
Я бы посчитал ошибкой то, что обнаружение изменений EF (DetectChanges
), похоже, изменяет свою собственную внутреннюю коллекцию, через которую он просто перебирает.
Теперь решение этой проблемы легко: просто 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();
EF добавит весь связанный объектный граф в контекст. Этот код выполнен успешно (также используется SaveChanges
вместо DetectChanges
).
Или ваш пример:
using (DbContext db = DbContext.GetNewDbContext()){
Employee creator = new Employee("Bob");
Employer employer = new Employer("employer", creator);
db.Employees.Add(creator);
db.SaveChanges();
}
Редактировать
Это было то же исключение: Entity Framework выдает «Коллекция была изменена» при использовании наблюдаемой коллекции . Следуя коду в этом примере, ситуация была аналогичной: добавление объекта в контекст, а затем изменение / добавление связей к этому объекту.
Edit2
Интересно, что это бросает то же исключение:
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
Итак, я хочу добавить отношения с новыми сущностями к существующей сущности. Решение этой проблемы кажется менее очевидным.