Исключение: На объект сущности не могут ссылаться несколько экземпляров интерфейса IEntityChangeTracker

92
26 ноября 2020, 18:10

Использовался подход Database First.

Имеется часть кода:

using (DBContext db = new DBContext())
{
    currentAct = new Act()
    {
        ActNumber = actNumber,
        EntryDateTime = DateTime.Now,
        Employee = currentEmployee
    };
    db.Acts.Add(currentAct); // здесь ловлю исключение!
    db.SaveChanges();
} 

По каким вообще причинам может вылетать это исключение??

Answer 1

Проблема в том, что ваш currentEmployee - не простой объект, а объект имеющий ссылку на контекст (точнее, на его Change Tracker, но это почти одно и то же). Отсюда и проблема: такой объект не может одновременно находиться в двух разных контекстах. Отдельно отмечу, что два разных контекста - это два разных объекта, а не два разных класса. Также отмечу, что проблема может быть не только в currentEmployee, но и в любом объекте на который currentEmployee ссылается.

Способов исправления этой проблемы - целая куча.

Прежде всего, сущности открепляются от контекста при разрушении этого самого контекста:

using (var db = new DBContext()) 
{
    currentEmployee = db.Employees.Where(...).Single();
}
// Здесь currentEmployee уже можно использовать в другом контексте

Это - основной паттерн использования контекстов, и по возможности именно его следует использовать.

Если же по какой-то причине вы не можете разрушить контекст перед использованием currentEmployee - вы можете попытаться не создавать новый контекст, а использовать старый:

using (var db = new DBContext()) 
{
    currentEmployee = db.Employees.Where(...).Single();
    var act = new Act { ... };
    db.Acts.Add(act);
    db.SaveChanges();
}

Если этот вариант также невозможен - то вы можете открепить currentEmployee от контекста перед тем как его куда-то сохранять:

currentEmployee = db.Employees.Where(...).Single();
db.Entry(currentEmployee).State = EntityState.Detached;

С той же целью можно использовать метод AsNoTracking:

currentEmployee = db.Employees.AsNoTracking().Where(...).Single();

Далее, можно выключить создание прокси-классов, это лишит currentEmployee ссылки на контекст, позволив добавлять его к неограниченному числу контекстов:

using (var db = new DBContext()) 
{
    db.Configuration.ProxyCreationEnabled = false;
    currentEmployee = db.Employees.Where(...).Single();
}

Наконец, можно запретить EF создавать прокси для конкретного класса сущности, если объявить этот класс как sealed:

public sealed class Employee
{
    // ...
}

PS Использовав любое из приведенных решений (кроме второго), вы столкнетесь с ещё одной проблемой: EF не будет знать, что currentEmployee уже лежит в базе данных, и попытается добавить эту сущность в базу ещё раз.

Эта проблема решается через метод Attach:

using (DBContext db = new DBContext())
{
    db.Employees.Attach(currentEmployee);
    var currentAct = new Act
    {
        Employee = currentEmployee
        // ...
    };
    db.Acts.Add(currentAct);
    db.SaveChanges();
} 

Альтернативная форма - через модификацию Entry State:

db.Entry(currentEmployee).State = EntityState.Unchanged;
READ ALSO
Краш MySQL в XAMPP

Краш MySQL в XAMPP

Пытаюсь запустить MySQL в XAMPP но она постоянно крашиться

194
Проверить существование файла glob php

Проверить существование файла glob php

Подскажите пожалуйста, есть такой код:

150
Проблема JQUERY в цикле PHP

Проблема JQUERY в цикле PHP

Подскажите, пожалуйста, я вывожу статьи и комментарии к ним в цикле PHP foreach

113