[c#] Entity Framework가 자식 개체를 저장 / 삽입하지 못하도록하려면 어떻게해야합니까?

엔터티 프레임 워크로 엔터티를 저장할 때 자연스럽게 지정된 엔터티 만 저장하려고 할 것이라고 가정했습니다. 그러나 해당 엔터티의 하위 엔터티도 저장하려고합니다. 이로 인해 모든 종류의 무결성 문제가 발생합니다. 저장하려는 엔터티 만 저장하고 모든 자식 개체를 무시하도록 EF를 강제하는 방법은 무엇입니까?

속성을 수동으로 null로 설정하면 “작업 실패 : 하나 이상의 외래 키 속성이 null이 아니기 때문에 관계를 변경할 수 없습니다.”라는 오류가 발생합니다. 자식 개체를 특별히 null로 설정하여 EF가 그대로두기 때문에 이것은 매우 비생산적입니다.

자식 개체를 저장 / 삽입하지 않는 이유는 무엇입니까?

이것은 의견에서 앞뒤로 논의되고 있으므로 왜 내 자식 개체를 그대로두기를 원하는지에 대한 정당성을 제공하겠습니다.

내가 빌드중인 애플리케이션에서 EF 개체 모델은 데이터베이스에서로드되지 않고 플랫 파일을 구문 분석하는 동안 채우는 데이터 개체로 사용됩니다. 자식 개체의 경우 이러한 개체 중 다수는 부모 테이블의 다양한 속성을 정의하는 조회 테이블을 참조합니다. 예를 들어 기본 엔티티의 지리적 위치입니다.

이러한 개체를 직접 채웠으므로 EF는 이러한 개체를 새 개체로 가정하고 부모 개체와 함께 삽입해야합니다. 그러나 이러한 정의는 이미 존재하며 데이터베이스에 중복을 생성하고 싶지 않습니다. EF 개체를 사용하여 조회를 수행하고 주 테이블 엔터티에 외래 키를 채 웁니다.

실제 데이터 인 자식 개체를 사용하더라도 부모를 먼저 저장하고 기본 키를 가져와야합니다. 그렇지 않으면 EF가 엉망인 것 같습니다. 이것이 약간의 설명을 제공하기를 바랍니다.



답변

내가 아는 한 두 가지 옵션이 있습니다.

옵션 1)

모든 자식 개체가 Null이면 EF가 아무것도 추가하지 않도록합니다. 또한 데이터베이스에서 아무것도 삭제하지 않습니다.

옵션 2)

다음 코드를 사용하여 자식 개체를 컨텍스트에서 분리 된 것으로 설정합니다.

 context.Entry(yourObject).State = EntityState.Detached

List/는 분리 할 수 ​​없습니다 Collection. 목록을 반복하고 목록의 각 항목을 이렇게 분리해야합니다.

foreach (var item in properties)
{
     db.Entry(item).State = EntityState.Detached;
}


답변

간단히 말해서 외래 키를 사용하면 하루를 절약 할 수 있습니다.

학교 법인과 도시 법인 이 있다고 가정 하고 이것은 도시에 많은 학교가 있고 학교가 도시에 속하는 다 대일 관계입니다. 그리고 도시가 이미 조회 테이블에 존재하므로 새 학교를 삽입 할 때 도시가 다시 삽입되는 것을 원하지 않습니다.

처음에는 다음과 같이 엔티티를 정의 할 수 있습니다.

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class School
{
    public int Id { get; set; }
    public string Name { get; set; }

    [Required]
    public City City { get; set; }
}

그리고 다음 과 같이 School 삽입을 수행 할 수 있습니다 (이미 newItem에 City 속성이 할당되어 있다고 가정 ).

public School Insert(School newItem)
{
    using (var context = new DatabaseContext())
    {
        context.Set<School>().Add(newItem);
        // use the following statement so that City won't be inserted
        context.Entry(newItem.City).State = EntityState.Unchanged;
        context.SaveChanges();
        return newItem;
    }
}

위의 접근 방식은이 경우 완벽하게 작동 할 수 있지만, 더 명확하고 유연한 외래 키 접근 방식을 선호합니다 . 아래 업데이트 된 솔루션을 참조하십시오.

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class School
{
    public int Id { get; set; }
    public string Name { get; set; }

    [ForeignKey("City_Id")]
    public City City { get; set; }

    [Required]
    public int City_Id { get; set; }
}

이러한 방식으로 학교 에 외래 키 City_Id 가 있고 City 엔티티를 참조하도록 명시 적으로 정의합니다 . 따라서 School 삽입과 관련하여 다음을 수행 할 수 있습니다.

    public School Insert(School newItem, int cityId)
    {
        if(cityId <= 0)
        {
            throw new Exception("City ID no provided");
        }

        newItem.City = null;
        newItem.City_Id = cityId;

        using (var context = new DatabaseContext())
        {
            context.Set<School>().Add(newItem);
            context.SaveChanges();
            return newItem;
        }
    }

이 경우 새 레코드 의 City_Id 를 명시 적으로 지정하고 그래프 에서 City 를 제거하여 EF가 School 과 함께 컨텍스트에 추가하지 않도록합니다 .

첫인상에서는 외래 키 접근 방식이 더 복잡해 보이지만 다 대다 관계를 삽입 할 때이 사고 방식을 사용하면 많은 시간을 절약 할 수 있습니다 (학교와 학생 관계, 학생 City 속성이 있음) 등등.

이것이 당신에게 도움이되기를 바랍니다.


답변

그냥 가게 a를 변경하려는 경우 부모 객체와 그의를 보관하지 마십시오 변경 자식 개체, 왜 그냥 다음을 수행하지 :

using (var ctx = new MyContext())
{
    ctx.Parents.Attach(parent);
    ctx.Entry(parent).State = EntityState.Added;  // or EntityState.Modified
    ctx.SaveChanges();
}

첫 번째 줄은 상위 개체와 종속 하위 개체의 전체 그래프를 Unchanged상태 의 컨텍스트에 연결 합니다.

두 번째 줄은 부모 개체의 상태 만 변경하고 자식은 그대로 둡니다 Unchanged.

새로 생성 된 컨텍스트를 사용하므로 데이터베이스에 다른 변경 사항이 저장되지 않습니다.


답변

제안 된 솔루션 중 하나는 동일한 데이터베이스 컨텍스트에서 탐색 속성을 할당하는 것입니다. 이 솔루션에서는 데이터베이스 컨텍스트 외부에서 할당 된 탐색 속성이 대체됩니다. 그림은 다음 예를 참조하십시오.

class Company{
    public int Id{get;set;}
    public Virtual Department department{get; set;}
}
class Department{
    public int Id{get; set;}
    public String Name{get; set;}
}

데이터베이스에 저장 :

 Company company = new Company();
 company.department = new Department(){Id = 45};
 //an Department object with Id = 45 exists in database.    

 using(CompanyContext db = new CompanyContext()){
      Department department = db.Departments.Find(company.department.Id);
      company.department = department;
      db.Companies.Add(company);
      db.SaveChanges();
  }

Microsoft는 이것을 기능으로 등록하지만 나는 이것이 성가시다. 회사 개체와 연결된 부서 개체에 이미 데이터베이스에있는 ID가있는 경우 EF가 회사 개체를 데이터베이스 개체와 연결하지 않는 이유는 무엇입니까? 우리가 연합을 스스로 돌봐야하는 이유는 무엇입니까? 새 개체를 추가하는 동안 탐색 속성을 관리하는 것은 데이터베이스 작업을 SQL에서 C #으로 이동하는 것과 같이 개발자에게 번거 롭습니다.


답변

먼저 EF에서 엔터티를 업데이트하는 두 가지 방법이 있음을 알아야합니다.

  • 부착 된 개체

위에서 설명한 방법 중 하나를 사용하여 개체 컨텍스트에 연결된 개체의 관계를 변경할 때 Entity Framework는 외래 키, 참조 및 컬렉션을 동기화 상태로 유지해야합니다.

  • 연결이 끊긴 개체

연결이 끊어진 개체로 작업하는 경우 동기화를 수동으로 관리해야합니다.

내가 빌드중인 애플리케이션에서 EF 개체 모델은 데이터베이스에서로드되지 않고 플랫 파일을 구문 분석하는 동안 채우는 데이터 개체로 사용됩니다.

즉, 연결이 끊어진 개체로 작업하고 있지만 독립 연결을 사용하는지 외래 키 연결을 사용하는지 명확하지 않습니다 .

  • 더하다

    기존 자식 개체 (데이터베이스에있는 개체)와 함께 새 엔터티를 추가 할 때 자식 개체가 EF에서 추적되지 않으면 자식 개체가 다시 삽입됩니다. 먼저 자식 개체를 수동으로 연결하지 않는 한.

      db.Entity(entity.ChildObject).State = EntityState.Modified;
      db.Entity(entity).State = EntityState.Added;
  • 최신 정보

    엔터티를 수정 된 것으로 표시하면 모든 스칼라 속성이 업데이트되고 탐색 속성은 무시됩니다.

      db.Entity(entity).State = EntityState.Modified;

그래프 차이

연결이 끊어진 객체로 작업 할 때 코드를 단순화하려면 diff 라이브러리그래프로 표시해 볼 수 있습니다 .

다음은 소개, Introducing GraphDiff for Entity Framework Code First-Allowing automatic updates of a graph of detached entities .

샘플 코드

  • 엔티티가 없으면 삽입하고 그렇지 않으면 업데이트합니다.

      db.UpdateGraph(entity);
  • 그것은, 그렇지 않으면 업데이트 존재하지 않는 개체 삽입 존재, 그렇지 않으면 갱신하지 않는 경우 자식 개체 삽입.

      db.UpdateGraph(entity, map => map.OwnedEntity(x => x.ChildObject));


답변

이를 수행하는 가장 좋은 방법은 데이터 컨텍스트에서 SaveChanges 함수를 재정의하는 것입니다.

    public override int SaveChanges()
    {
        var added = this.ChangeTracker.Entries().Where(e => e.State == System.Data.EntityState.Added);

        // Do your thing, like changing the state to detached
        return base.SaveChanges();
    }


답변

프로필을 저장하려고 할 때 동일한 문제가 있습니다. 이미 테이블 인사말과 프로필을 새로 만들었습니다. 프로필을 삽입하면 인사말에도 삽입됩니다. 그래서 savechanges () 전에 이렇게 시도했습니다.

db.Entry (Profile.Salutation) .State = EntityState.Unchanged;