관계, 탐색 속성 및 외래 키

이 문서에서는 Entity Framework가 엔터티 간의 관계를 관리하는 방법에 대한 개요를 제공합니다. 또한 관계를 매핑하고 조작하는 방법에 대한 몇 가지 지침을 제공합니다.

EF의 관계

관계형 데이터베이스에서 테이블 간의 관계(연결이라고도 함)는 외래 키를 통해 정의됩니다. FK(외래 키)는 두 테이블의 데이터 간 연결을 설정하고 강제 적용하는 데 사용되는 열입니다. 일반적으로 일대일, 일대다 및 다대다의 세 가지 유형의 관계가 있습니다. 일대다 관계에서 외래 키는 관계의 여러 끝을 나타내는 테이블에 정의됩니다. 다대다 관계에는 세 번째 테이블(접합 또는 조인 테이블이라고 함)을 정의하는 작업이 포함되며, 기본 키는 두 관련 테이블의 외래 키로 구성됩니다. 일대일 관계에서 기본 키는 외래 키로 추가로 작동하며 두 테이블에 대해 별도의 외래 키 열이 없습니다.

다음 이미지는 일대다 관계에 참여하는 두 테이블을 보여줍니다. 과정 테이블은 Department 테이블에 연결하는 DepartmentID 열을 포함하기 때문에 종속 테이블입니다.

Department and Course tables

Entity Framework에서 엔터티는 연결 또는 관계를 통해 다른 엔터티와 관련될 수 있습니다. 각 관계에는 엔터티 형식과 해당 관계의 두 엔터티에 대한 형식의 다중성(1, 0 또는 1 또는 다)을 설명하는 두 개의 끝이 포함됩니다. 관계는 참조 제약 조건으로 제어할 수 있으며, 이 조건에서는 관계의 주 역할과 종속 역할이 각각 어느 쪽 끝인지를 지정합니다.

탐색 속성을 사용하면 두 엔터티 형식 간의 연결을 탐색할 수 있습니다. 모든 개체에는 해당 개체가 참여하는 모든 관계에 대한 탐색 속성이 있습니다. 탐색 속성을 사용하면 양방향에서 관계를 탐색 및 관리하고, 참조 개체(다중성이 1인 경우 또는 0이나 1인 경우)나 개체 컬렉션(다중성이 다수인 경우)을 반환할 수 있습니다. 단방향 탐색을 선택할 수도 있으며, 이 경우 관계에 참여하는 형식 중 하나에만 탐색 속성을 정의하고 둘 다에서 정의하지 않을 수 있습니다.

데이터베이스의 외래 키에 매핑되는 속성을 모델에 포함하는 것이 좋습니다. 외래 키 속성이 포함되어 있으면 종속 개체에서 외래 키 값을 수정하여 관계를 만들거나 변경할 수 있습니다. 이러한 연결 종류를 외래 키 연결이라고 합니다. 연결이 끊긴 엔터티로 작업할 때 외래 키를 사용하는 것이 훨씬 더 중요합니다. 일대일 또는 일대0으로 작업하는 경우 1 관계에는 별도의 외래 키 열이 없으며 기본 키 속성은 외래 키 역할을 하고 항상 모델에 포함됩니다.

개념적 모델에 외래 키 열이 포함되어 있지 않은 경우 연결 정보는 독립적 개체로 관리됩니다. 관계는 외래 키 속성 대신 개체 참조를 통해 추적됩니다. 이러한 유형의 연결을 독립 연결이라고 합니다. 독립 연결을 수정하는 가장 일반적인 방법은 연결에 참여하는 각 엔터티에 생성되는 탐색 속성을 수정하는 것입니다.

모델에서 두 종류의 연결 중 하나 또는 둘 모두를 사용할 수 있습니다. 그러나 외래 키만 포함하는 조인 테이블에 의해 연결된 순수한 다대다 관계가 있는 경우 EF는 독립 연결을 사용하여 이러한 다대다 관계를 관리합니다.   

다음 이미지는 Entity Framework 디자이너를 사용하여 만든 개념적 모델을 보여줍니다. 모델에는 일대다 관계에 참여하는 두 엔터티가 포함되어 있습니다. 두 엔터티 모두 탐색 속성이 있습니다. Course는 종속 엔터티이며 DepartmentID 외래 키 속성이 정의되어 있습니다.

Department and Course tables with navigation properties

다음 코드 조각은 Code First를 사용하여 만든 모델과 동일한 모델을 보여줍니다.

public class Course
{
  public int CourseID { get; set; }
  public string Title { get; set; }
  public int Credits { get; set; }
  public int DepartmentID { get; set; }
  public virtual Department Department { get; set; }
}

public class Department
{
   public Department()
   {
     this.Courses = new HashSet<Course>();
   }  
   public int DepartmentID { get; set; }
   public string Name { get; set; }
   public decimal Budget { get; set; }
   public DateTime StartDate { get; set; }
   public int? Administrator {get ; set; }
   public virtual ICollection<Course> Courses { get; set; }
}

관계 구성 또는 매핑

이 페이지의 나머지 부분에는 관계를 사용하여 데이터에 액세스하고 조작하는 방법이 설명되어 있습니다. 모델에서 관계를 설정하는 방법에 대한 자세한 내용은 다음 페이지를 참조하세요.

관계 만들기 및 수정

외래 키 연결에서 관계를 변경하면 EntityState.Unchanged 상태인 종속 개체의 상태가 EntityState.Modified로 변경됩니다. 독립 관계에서는 관계를 변경해도 종속 개체의 상태가 업데이트되지 않습니다.

다음 예제에서는 외래 키 속성과 탐색 속성을 사용하여 관련 개체를 연결하는 방법을 보여줍니다. 외래 키 연결을 사용할 경우 두 방법 중 하나를 사용하여 관계를 만들거나 수정할 수 있습니다. 독립 연결을 사용할 경우에는 외래 키 속성을 사용할 수 없습니다.

  • 외래 키 연결에서는 다음 예제와 같이 외래 키 속성에 새 값을 할당할 수 있습니다.

    course.DepartmentID = newCourse.DepartmentID;
    
  • 다음 코드는 외래 키를 null로 설정하여 관계를 제거합니다. 외래 키 속성은 null 허용이어야 합니다.

    course.DepartmentID = null;
    

    참고 항목

    참조가 추가된 상태(이 예제에서는 과정 개체)인 경우 SaveChanges가 호출될 때까지 참조 탐색 속성이 새 개체의 키 값과 동기화되지 않습니다. 개체 컨텍스트에는 추가된 개체에 대한 영구 키가 포함되어 있지 않으므로 해당 개체가 저장되기 전까지는 동기화가 수행되지 않습니다. 관계를 설정하는 즉시 새 개체가 완전히 동기화되어야 하는 경우 다음 방법 중 하나를 사용합니다.*

  • 탐색 속성에 새 개체를 할당합니다. 다음 코드는 과정과 department 간의 관계를 만듭니다. 개체가 컨텍스트에 연결되어 있으면 coursedepartment.Courses 컬렉션에 추가되고 course 개체에 대한 해당 외래 키 속성은 부서의 키 속성 값으로 설정됩니다.

    course.Department = department;
    
  • 관계를 삭제하려면 탐색 속성을 null로 설정합니다. .NET 4.0을 기반으로 하는 Entity Framework를 사용하는 경우 null로 설정하기 전에 관련 끝을 로드해야 합니다. 예시:

    context.Entry(course).Reference(c => c.Department).Load();
    course.Department = null;
    

    .NET 4.5를 기반으로 하는 Entity Framework 5.0부터는 관련 끝을 로드하지 않고도 관계를 null로 설정할 수 있습니다. 다음 메서드를 사용하여 현재 값을 null로 설정할 수도 있습니다.

    context.Entry(course).Reference(c => c.Department).CurrentValue = null;
    
  • 엔터티 컬렉션에서 개체를 삭제하거나 추가합니다. 예를 들어 department.Courses 컬렉션에 Course 형식의 개체를 추가할 수 있습니다. 이 작업은 특정 과정 및 특정 department 간에 관계를 만듭니다. 개체가 컨텍스트에 연결되어 있으면 과정 개체의 부서 참조 및 외래 키 속성이 해당 department로 설정됩니다.

    department.Courses.Add(newCourse);
    
  • ChangeRelationshipState 메서드를 사용하여 두 엔터티 개체 간의 지정된 관계 상태를 변경합니다. 이 메서드는 N 계층 애플리케이션 및 독립 연결 작업을 할 때 가장 일반적으로 사용됩니다(외래 키 연결에는 사용할 수 없음). 또한 이 메서드를 사용하려면 아래 예제와 같이 ObjectContext로 드롭다운해야 합니다.
    다음 예제에는 강사와 과정 간에 다대다 관계가 있습니다. ChangeRelationshipState 메서드를 호출하고 EntityState.Added 매개 변수를 전달하면 SchoolContext 두 개체 간에 관계가 추가되었음을 알 수 있습니다.

    
    ((IObjectContextAdapter)context).ObjectContext.
      ObjectStateManager.
      ChangeRelationshipState(course, instructor, c => c.Instructor, EntityState.Added);
    

    관계를 추가하는 것뿐만 아니라 업데이트하는 경우 새 관계를 추가한 후 이전 관계를 삭제해야 합니다.

    ((IObjectContextAdapter)context).ObjectContext.
      ObjectStateManager.
      ChangeRelationshipState(course, oldInstructor, c => c.Instructor, EntityState.Deleted);
    

외래 키 및 탐색 속성 간의 변경 내용 동기화

위에 설명된 방법 중 하나를 사용하여 컨텍스트에 연결된 개체의 관계를 변경할 경우 Entity Framework에서는 외래 키, 참조 및 컬렉션의 동기화 상태를 유지해야 합니다. Entity Framework는 프록시를 사용하여 POCO 엔터티에 대한 이 동기화(관계 수정이라고도 함)를 자동으로 관리합니다. 자세한 내용은 프록시 작업을 참조하세요.

프록시가 없는 POCO 엔터티를 사용하는 경우 컨텍스트에서 관련 개체를 동기화하기 위해 DetectChanges 메서드가 호출되었는지 확인해야 합니다. 다음 API는 DetectChanges 호출을 자동으로 트리거합니다.

  • DbSet.Add
  • DbSet.AddRange
  • DbSet.Remove
  • DbSet.RemoveRange
  • DbSet.Find
  • DbSet.Local
  • DbContext.SaveChanges
  • DbSet.Attach
  • DbContext.GetValidationErrors
  • DbContext.Entry
  • DbChangeTracker.Entries
  • DbSet에 대해 LINQ 쿼리 실행

Entity Framework에서는 일반적으로 탐색 속성을 사용하여 정의된 연결에서 반환된 엔터티와 관련된 엔터티를 로드합니다. 자세한 내용은 관련 개체 로드를 참조하세요.

참고 항목

외래 키 연결에서 종속 개체의 관련된 끝을 로드하면 현재 메모리에 있는 종속 개체의 외래 키 값에 따라 관련 개체가 로드됩니다.

    // Get the course where currently DepartmentID = 2.
    Course course = context.Courses.First(c => c.DepartmentID == 2);

    // Use DepartmentID foreign key property
    // to change the association.
    course.DepartmentID = 3;

    // Load the related Department where DepartmentID = 3
    context.Entry(course).Reference(c => c.Department).Load();

독립 연결에서는 현재 데이터베이스에 있는 외래 키 값에 따라 종속 개체의 관련된 끝이 쿼리됩니다. 그러나 관계가 수정되었고 종속 개체의 참조 속성이 개체 컨텍스트에 로드된 다른 주 개체를 가리키는 경우 Entity Framework에서는 클라이언트에 정의된 대로 관계를 만들려고 합니다.

동시성 관리

외래 키 및 독립 연결 모두에서 동시성 검사는 모델에 정의된 엔터티 키 및 기타 엔터티 속성을 기반으로 합니다. EF 디자이너를 사용하여 모델을 만드는 경우 ConcurrencyMode 특성을 고정으로 설정하여 속성이 동시성을 검사하도록 지정합니다. Code First를 사용하여 모델을 정의하는 경우 동시성을 확인하려는 ConcurrencyCheck 속성에 주석을 사용합니다. Code First로 작업할 때 TimeStamp 주석을 사용하여 속성이 동시성을 검사하도록 지정할 수도 있습니다. 지정된 클래스에는 타임스탬프 속성이 하나만 있을 수 있습니다. Code First는 이 속성을 데이터베이스의 null을 허용하지 않는 필드에 매핑합니다.

동시성 검사 및 확인에 참여하는 엔터티로 작업할 때는 항상 외래 키 연결을 사용하는 것이 좋습니다.

자세한 내용은 동시성 충돌 처리를 참조하세요.

겹치는 키 사용

겹치는 키는 키의 일부 속성이 엔터티에 있는 다른 키의 일부이기도 한 복합 키입니다. 독립 연결에서는 겹치는 키를 사용할 수 없습니다. 겹치는 키가 포함된 외래 키 연결을 변경하려면 개체 참조를 사용하는 대신 외래 키 값을 수정하는 것이 좋습니다.