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

Я пытаюсь сохранить данные о сотрудниках, которые имеют ссылки на город. Но каждый раз, когда я пытаюсь сохранить свой контакт, который проверен, я получаю исключение "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);
    }

и файле employeeservice Код

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

10 ответов


потому что эти две линии ...

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 метод к методу этой службы.


шаги для воспроизведения могут быть упрощены для этого:

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

var user = contexOne.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();

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

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

тогда достаточно назначить:

e1.CityId=city1.ID;

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

Я использую инъекцию зависимостей для инъекции слоев сервиса/репозитория в контроллер и как таковой не имею доступа к контексту от контроллера.

мое решение состояло в том, чтобы слои сервиса/репозитория использовали один и тот же экземпляр context - 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;

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


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

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

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

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


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


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


в этом случае оказывается, что ошибка очень ясна: 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);

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

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

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