Объект объекта не может ссылаться на несколько экземпляров IEntityChangeTracker. при добавлении связанных объектов к объекту в Entity Framework 4.1

Я пытаюсь сохранить сведения о сотрудниках, которые имеют ссылки на City. Но каждый раз, когда я пытаюсь сохранить мой контакт, который проверяется, я получаю исключение "ADO.Net Entity Framework Объект сущности не может ссылаться на несколько экземпляров IEntityChangeTracker"

Я читал так много сообщений, но до сих пор не понял, что делать... мой код кнопки "Сохранить" приведен ниже.

protected void Button1_Click(object sender, EventArgs e)
    {
        EmployeeService es = new EmployeeService();
        CityService cs = new CityService();

        DateTime dt = new DateTime(2008, 12, 12);
        Payroll.Entities.Employee e1 = new Payroll.Entities.Employee();

        Payroll.Entities.City city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));

        e1.Name = "Archana";
        e1.Title = "aaaa";
        e1.BirthDate = dt;
        e1.Gender = "F";
        e1.HireDate = dt;
        e1.MaritalStatus = "M";
        e1.City = city1;        

        es.AddEmpoyee(e1,city1);
    }

и Код обслуживания сотрудников

public string AddEmpoyee(Payroll.Entities.Employee e1, Payroll.Entities.City c1)
        {
            Payroll_DAO1 payrollDAO = new Payroll_DAO1();
            payrollDAO.AddToEmployee(e1);  //Here I am getting Error..
            payrollDAO.SaveChanges();
            return "SUCCESS";
        }

Ответ 1

Потому что эти две строки...

EmployeeService es = new EmployeeService();
CityService cs = new CityService();

... не принимать параметр в конструкторе, я предполагаю, что вы создаете контекст внутри классов. Когда вы загружаете city1...

Payroll.Entities.City city1 = cs.SelectCity(...);

... вы прикрепляете city1 к контексту в CityService. Позже вы добавите city1 в качестве ссылки на новый Employee e1 и добавьте e1, включая эту ссылку, в city1 в контекст в EmployeeService. В результате у вас есть city1, привязанный к двум различным контекстам, о чем жалуется исключение.

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

EmployeeService es = new EmployeeService(context);
CityService cs = new CityService(context); // same context instance

Ваши классы обслуживания выглядят немного похожими на репозитории, которые отвечают только за один тип сущности. В таком случае у вас всегда будут проблемы, как только будут связаны отношения между сущностями, когда вы используете отдельные контексты для служб.

Вы также можете создать единую службу, которая отвечает за набор тесно связанных объектов, таких как EmployeeCityService (который имеет один контекст) и делегировать всю операцию в вашем методе Button1_Click методу этой службы.

Ответ 2

Шаги для воспроизведения можно упростить до этого:

var contextOne = new EntityContext();
var contextTwo = new EntityContext();

var user = contextOne.Users.FirstOrDefault();

var group = new Group();
group.User = user;

contextTwo.Groups.Add(group);
contextTwo.SaveChanges();

Код без ошибок:

var context = new EntityContext();

var user = context.Users.FirstOrDefault();

var group = new Group();
group.User = user; // Be careful when you set entity properties. 
// Be sure that all objects came from the same context

context.Groups.Add(group);
context.SaveChanges();

Использование только одного EntityContext может решить эту проблему. Обратитесь к другим ответам для других решений.

Ответ 3

Это старый поток, но другое решение, которое я предпочитаю, это просто обновить cityId и не назначать модель дырки City для Employee..., чтобы Employee должен выглядеть следующим образом:

public class Employee{
    ...
    public int? CityId; //The ? is for allow City nullable
    public virtual City City;
}

Тогда достаточно присваивать:

e1.CityId=city1.ID;

Ответ 4

У меня была та же проблема, но моя проблема с решением @Slauma (хотя в некоторых случаях это здорово) заключается в том, что он рекомендует передать контекст в службу, что подразумевает, что контекст доступен из моего контроллера. Это также вызывает тесную связь между моим контроллером и сервисными уровнями.

Я использую Dependency Injection, чтобы внедрить слои сервиса/репозитория в контроллер и поэтому не имею доступа к контексту из контроллера.

Мое решение состояло в том, чтобы слои сервиса/репозитория использовали один и тот же экземпляр контекста - Singleton.

Класс синглтона контекста:

Ссылка: http://msdn.microsoft.com/en-us/library/ff650316.aspx
и http://csharpindepth.com/Articles/General/Singleton.aspx

public sealed class MyModelDbContextSingleton
{
  private static readonly MyModelDbContext instance = new MyModelDbContext();

  static MyModelDbContextSingleton() { }

  private MyModelDbContextSingleton() { }

  public static MyModelDbContext Instance
  {
    get
    {
      return instance;
    }
  }
}  

Класс репозитория:

public class ProjectRepository : IProjectRepository
{
  MyModelDbContext context = MyModelDbContextSingleton.Instance;
  [...]

Существуют и другие решения, такие как создание экземпляра контекста один раз и передача его в конструкторы уровней вашего сервиса/хранилища, или другое, о котором я читал, реализующее шаблон Unit of Work. Я уверен, что есть еще...

Ответ 5

В качестве альтернативы для инъекций и, что еще хуже, Singleton, вы можете вызвать метод Отсоединить до добавления.

EntityFramework 6: ((IObjectContextAdapter)cs).ObjectContext.Detach(city1);

EntityFramework 4: cs.Detach(city1);

Существует еще один способ, если вам не нужен первый объект DBContext. Просто оберните его ключевым словом с помощью:

Payroll.Entities.City city1;
using (CityService cs = new CityService())
{
  city1 = cs.SelectCity(Convert.ToInt64(cmbCity.SelectedItem.Value));
}

Ответ 6

В моем случае я использовал ASP.NET Identity Framework. Я использовал встроенный метод UserManager.FindByNameAsync для получения объекта ApplicationUser. Затем я попытался ссылаться на этот объект на вновь созданном объекте на другом DbContext. Это привело к исключению, которое вы изначально видели.

Я решил это, создав новый объект ApplicationUser только с Id из метода UserManager и ссылаясь на этот новый объект.

Ответ 7

У меня была такая же проблема, и я мог решить создать новый экземпляр объекта, который я пытался обновить. Затем я передал этот объект в свой репозиторий.

Ответ 8

В этом случае оказывается, что ошибка предельно ясна: Entity Framework не может отслеживать сущность, используя несколько экземпляров IEntityChangeTracker или, как правило, несколько экземпляров DbContext. Решения: использовать один экземпляр DbContext; получить доступ ко всем необходимым объектам через один репозиторий (в зависимости от одного экземпляра DbContext); или отключение отслеживания для всех сущностей, к которым обращаются через репозиторий, отличный от того, который выбрасывает это конкретное исключение.

Следуя инверсии шаблона управления в .Net Core Web API, я часто обнаруживаю, что у меня есть контроллеры с такими зависимостями, как:

private readonly IMyEntityRepository myEntityRepo; // depends on MyDbContext
private readonly IFooRepository fooRepo; // depends on MyDbContext
private readonly IBarRepository barRepo; // depends on MyDbContext
public MyController(
    IMyEntityRepository myEntityRepo, 
    IFooRepository fooRepo, 
    IBarRepository barRepo)
{
    this.fooRepo = fooRepo;
    this.barRepo = barRepo;
    this.myEntityRepo = myEntityRepo;
}

и использование как

...
myEntity.Foo = await this.fooRepository.GetFoos().SingleOrDefaultAsync(f => f.Id == model.FooId);
if (model.BarId.HasValue)
{
    myEntity.Foo.Bar = await this.barRepository.GetBars().SingleOrDefaultAsync(b => b.Id == model.BarId.Value);
}

...
await this.myEntityRepo.UpdateAsync(myEntity); // this throws an error!

Поскольку все три репозитория зависят от разных экземпляров DbContext на запрос, у меня есть два варианта, чтобы избежать проблемы и поддерживать отдельные репозитории: изменить внедрение DbContext, чтобы создать новый экземпляр только один раз за вызов:

// services.AddTransient<DbContext, MyDbContext>(); <- one instance per ctor. bad
services.AddScoped<DbContext, MyDbContext>(); // <- one instance per call. good!

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

myEntity.Foo.Bar = await this.barRepo.GetBars().AsNoTracking().SingleOrDefault(b => b.Id == model.BarId);

Ответ 9

Использовать тот же объект DBContext во всей транзакции.

Ответ 10

Источник ошибки:

ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.Name);
ApplicationDbContext db = new ApplicationDbContent();
db.Users.Uploads.Add(new MyUpload{FileName="newfile.png"});
await db.SavechangesAsync();/ZZZZZZZ

Надеюсь, что кто-то сэкономит драгоценное время