여러 IEntityChangeTracker 인스턴스에서 엔티티 오브젝트를 참조 할 수 없습니다. Entity Framework 4.1에서 관련 개체를 엔터티에 추가하는 동안


165

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

답변:


241

이 두 줄 때문에 ...

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메소드 의 전체 조작 을이 서비스의 메소드에 위임 할 수도 있습니다.


4
답변에 배경 정보가 포함되어 있지 않더라도 알아 낸 방식이 마음에 듭니다.
Daniel Kmak

이것이 내 문제를 해결할 것 같습니다, 나는 새로운 컨텍스트 인스턴스를 작성하는 방법을 전혀 모른다 :(
Ortund

12
ORM을 추상화하는 것은 똥에 노란 립스틱을 바르는 것과 같습니다.
Ronnie Overby

여기에 뭔가 빠졌을 수도 있지만 일부 ORM (특히 EntityFramework)에서는 데이터 컨텍스트가 항상 짧아야합니다. 정적이거나 재사용되는 컨텍스트를 도입하면 다른 모든 문제와 문제가 발생합니다.
Maritim

@Maritim 사용법에 따라 다릅니다. 웹 응용 프로그램에서는 일반적으로 한 번의 왕복입니다. 데스크톱 응용 프로그램에서는 스레드 안전을 보장하지 않기 때문에 Form하나당 하나 (작업 단위를 나타내는 것만) 를 사용할 수도 있습니다. ThreadDbContext
LuckyLikey

30

재현 단계는 다음과 같이 단순화 할 수 있습니다.

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이 문제를 해결할 수 있습니다. 다른 솔루션에 대해서는 다른 답변을 참조하십시오.


2
contextTwo를 사용하고 싶다고 말할 수 있습니까? (범위 문제 또는 무언가로 인해 발생할 수 있음) contextOne에서 어떻게 분리하고 contextTwo에 연결합니까?
NullVoxPopuli

만약 당신이 이런 식으로해야한다면, 아마도 당신은 잘못된 방법으로 이것을하고있을 것입니다 ... 나는 한 가지 맥락을 사용하는 것이 좋습니다.
Pavel Shkleinik

3
다른 데이터베이스를 가리킬 때와 같이 다른 인스턴스를 사용하려는 인스턴스가 있습니다.
Jay

1
이것은 문제를 단순화하는 데 도움이됩니다. 그러나 실제 답변을 제공하지는 않습니다.
BrainSlugs83

9

이것은 오래된 스레드이지만 선호하는 또 다른 솔루션은 cityId를 업데이트하고 구멍 모델 City를 Employee에 할당하지 않는 것입니다.

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

그런 다음 충분하게 할당하십시오.

e1.CityId=city1.ID;

5

주입 및 더 나쁜 Singleton 대신 Add 전에 Detach 메서드를 호출 할 수 있습니다 .

엔티티 프레임 워크 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));
}

1
나는 다음을 사용했다 : dbContext1.Entry(backgroundReport).State = System.Data.Entity.EntityState.Detached'분리하고 dbContext2.Entry(backgroundReport).State = System.Data.Entity.EntityState.Modified;업데이트 에 사용할 수있었습니다 . 꿈처럼 일했습니다
Peter Smith

예, 피터 State를 Modified로 표시한다고 언급해야합니다.
Roman O

내 응용 프로그램 시작 (global.asax) 논리에서 위젯 목록을로드하고있었습니다. 메모리에 숨겨져있는 간단한 참조 객체 목록. Using 문 내에서 EF 컨텍스트를 수행하고 있었기 때문에 나중에 컨트롤러가 해당 개체를 비즈니스 그래프에 할당하는 데 문제가 없다고 생각했습니다 (이전 컨텍스트가 사라졌습니다)?-이 답변은 저를 구했습니다. .
bkwdesign

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;
  [...]

컨텍스트를 한 번 인스턴스화하여 서비스 / 저장소 계층의 생성자 또는 작업 단위 패턴을 구현하는 다른 솔루션으로 전달하는 것과 같은 다른 솔루션이 있습니다. 더 많은 것이 있다고 확신합니다 ...


9
... 멀티 스레딩을 사용하려고하자마자이 문제가 발생하지 않습니까?
CaffGeek

8
컨텍스트는 필요 이상으로 열려 있지 않아야합니다. 싱글 톤을 사용하여 영원히 열어 두는 것이 가장 마지막입니다.
enzi

3
요청 마다이 구현이 훌륭했습니다. Static 키워드를 사용하는 것은 잘못되었지만 요청이 시작될 때 컨텍스트를 인스턴스화하고 요청이 끝날 때 처리하도록이 패턴을 만들면 합법적 인 솔루션이됩니다.
Aidin

1
이것은 정말 나쁜 조언입니다. DI를 사용하고 있다면 (여기에서 증거가 보이지 않습니까?) DI ​​컨테이너가 컨텍스트 수명을 관리하게해야하며 요청마다 가능해야합니다.
케이시

3
이것은 나쁘다. 나쁜. 나쁜. 나쁜. 나쁜. 정적 객체는 모든 스레드와 사용자간에 공유되므로 특히 웹 응용 프로그램 인 경우 특히 그렇습니다. 즉, 웹 사이트의 여러 동시 사용자가 데이터 컨텍스트에서 스톰 핑하여 잠재적으로 데이터를 손상 시키거나 의도하지 않은 변경 사항을 저장하거나 임의의 충돌을 일으킬 수도 있습니다. DbContext는 스레드간에 공유해서는 안됩니다. 그러면 스태틱이 절대로 파괴되지 않는 문제가 있습니다. 그래서 더 많은 메모리를 사용하고 계속 사용할 것입니다.
Erik Funkenbusch

3

필자의 경우 ASP.NET Identity Framework를 사용하고있었습니다. 엔티티 UserManager.FindByNameAsync를 검색하기 위해 내장 메소드를 사용했습니다 ApplicationUser. 그런 다음 다른 엔터티에서 새로 만든 엔터티에서이 엔터티를 참조하려고했습니다 DbContext. 이로 인해 원래 본 예외가 발생했습니다.

메소드 ApplicationUser에서만 Idfrom을 사용하여 새 엔티티를 작성하고 UserManager해당 새 엔티티를 참조 하여이를 해결했습니다 .


1

나는 같은 문제가 있었고 업데이트하려고하는 객체의 새 인스턴스를 만들 수 있습니다. 그런 다음 그 대상을 내 철학자에게 전달했습니다.


샘플 코드를 도와주세요. ? 그래서 당신이 말하려는 것이 분명 할 것입니다
BJ Patel

1

이 경우 오류가 매우 분명합니다. 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);


0

프로젝트 (ASP.Net MVC EF6.2)에 IoC를 구현 한 후에도 동일한 문제가 발생했습니다.

일반적으로 컨트롤러 생성자에서 데이터 컨텍스트를 초기화하고 동일한 컨텍스트를 사용하여 모든 리포지토리를 초기화합니다.

그러나 IoC를 사용하여 리포지토리를 인스턴스화하면 모두 별도의 컨텍스트가 생겨서이 오류가 발생하기 시작했습니다.

지금은 더 나은 방법을 생각하면서 일반적인 컨텍스트로 리포지토리를 새로 업데이트하는 것으로 돌아갔습니다.


0

이것이 내가이 문제를 겪은 방법입니다. 먼저 OrderApplicationUser테이블에 대한 참조가 필요한 내 파일을 저장해야 합니다.

  ApplicationUser user = new ApplicationUser();
  user = UserManager.FindById(User.Identity.GetUserId());

  Order entOrder = new Order();
  entOrder.ApplicationUser = user; //I need this user before saving to my database using EF

문제는 새 Order엔터티 를 저장하기 위해 새 ApplicationDbContext를 초기화하고 있다는 것입니다 .

 ApplicationDbContext db = new ApplicationDbContext();
 db.Entry(entOrder).State = EntityState.Added;
 db.SaveChanges();

따라서 문제를 해결하기 위해 기본 제공 ASP.NET MVC의 UserManager를 사용하는 대신 동일한 ApplicationDbContext를 사용했습니다.

이 대신에 :

user = UserManager.FindById(User.Identity.GetUserId());

기존 ApplicationDbContext 인스턴스를 사용했습니다.

//db instance here is the same instance as my db on my code above.
user = db.Users.Find(User.Identity.GetUserId()); 

-2

오류 소스 :

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

누군가 소중한 시간을 절약하기를 바랍니다.


이것이 질문에 대한 대답인지 확실하지 않습니다. 아마도 어떤 맥락이 도움이 될 것입니다.
스튜어트 시글 러
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.