관계 정의 및 관리(Entity Framework)

Entity Framework 에서 엔터티는 연결을 통해 다른 엔터티와 관련될 수 있습니다. 이 엔터티 간의 관계는 개념적 모델에서 Association 요소로 정의됩니다. 각 관계에는 엔터티 형식과 형식의 복합성(1, 0이나 1, 또는 다수)을 표현하는 두 개의 끝이 포함됩니다. 관계는 참조 제약 조건으로 제어할 수 있으며, 이 조건은 관계의 주 역할과 종속 역할이 각각 어느 쪽 끝인지를 지정합니다.

.NET Framework 버전 4부터는 개념적 모델에 외래 키를 포함할 수 있습니다. 엔터티 데이터 모델 마법사의 모델에 외래 키 열 포함 옵션은 기본적으로 선택되어 있습니다. 이 옵션이 선택되어 있으면 생성된 엔터티 개체에 외래 키 열에 매핑하는 스칼라 속성이 포함됩니다. 외래 키 속성이 포함되어 있으면 종속 개체에서 외래 키 값을 수정하여 관계를 만들거나 변경할 수 있습니다. 이러한 연결 종류를 외래 키 연결이라고 합니다.

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

두 종류의 연결 모두에서 모든 개체에는 그 개체가 참여하는 모든 관계의 탐색 속성이 포함됩니다. 탐색 속성을 사용하면 양방향에서 관계를 탐색 및 관리하고, 참조 개체(복합성이 1인 경우 또는 0이나 1인 경우)나 개체 컬렉션(복합성이 다수인 경우)을 반환할 수 있습니다.

관계 혼합

모델에서 두 종류의 연결 중 하나 또는 둘 모두를 사용할 수 있습니다. 그러나 외래 키만 포함된 테이블(Pure Join Table이라고도 함)을 모델에 포함하면 모델에 외래 키 연결을 사용하도록 지정했더라도 순수한 다대다 관계를 관리하는 데 독립 연결이 사용됩니다. 엔터티 데이터 모델 마법사는 Pure Join Table에 매핑되는 엔터티를 만들지 않습니다.

관계 만들기 및 수정

Entity Framework 에서는 여러 가지 방법으로 관계를 만들고 수정할 수 있습니다.

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

    order.Customer = customer 
    
  2. 엔터티 컬렉션에서 개체를 삭제하거나 추가합니다. 예를 들어 Add 메서드를 사용하여 Order 형식의 개체를 customer.Orders 컬렉션에 추가할 수 있습니다. 이 작업에서는 특정 ordercustomer 간의 관계를 만듭니다. 개체가 개체 컨텍스트에 연결되어 있으면 order 개체에 대한 고객 참조와 외래 키 속성이 해당 customer로 설정됩니다.

    customer.Orders.Add(order)
    
  3. 외래 키 연결에서는 다음 예제와 같이 외래 키 속성에 새 값을 할당할 수 있습니다. 참조가 추가된 상태이면 SaveChanges가 호출될 때까지 참조 탐색 속성이 새 개체의 키 값과 동기화되지 않습니다. 개체 컨텍스트에는 추가된 개체에 대한 영구 키가 포함되어 있지 않으므로 해당 개체가 저장되기 전까지는 동기화가 수행되지 않습니다. 자세한 내용은 엔터티 키 사용(Entity Framework)을 참조하십시오. 관계를 설정하는 즉시 새 개체가 완전히 동기화되도록 해야 하는 경우에는 앞의 두 방법 중 하나를 사용합니다.

    order.CustomerID = newCustomer.CustomerID 
    order.CustomerID = null
    
  4. 특정 개체에 대한 엔터티 키를 만듭니다. 개체 컨텍스트에 이 키를 가진 개체가 이미 있으면 CreateEntityKey 메서드는 기존 개체의 EntityKey를 반환합니다. 이 메서드는 이전 버전인 .NET Framework 3.5 SP1과의 호환성을 위해 제공됩니다.

    order.CustomerReference.EntityKey = ctx.CreateEntityKey("EntitySetName", newObject)
    

위에 설명된 방법 중 하나를 사용하여 개체 컨텍스트에 연결된 개체의 관계를 변경할 경우 Entity Framework 에서는 외래 키, 참조 및 컬렉션의 동기화 상태를 유지해야 합니다. Entity Framework 에서는 다음과 같은 일부 형식의 적합한 개체에 대해 이와 같은 동기화를 관리합니다.

프록시가 없는 POCO 엔터티를 사용하는 경우에는 DetectChanges 메서드를 호출하여 개체 컨텍스트의 관련 개체를 동기화해야 합니다. 연결이 끊긴 개체를 사용하는 경우에는 동기화를 수동으로 관리해야 합니다.

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

' The following example creates a new StudentGrade object and associates 
' the StudentGrade with the Course and Person by 
' setting the foreign key properties. 

Using context As New SchoolEntities()
    ' The database will generate the EnrollmentID. 
    ' To create the association between the Course and StudentGrade, 
    ' and the Student and the StudentGrade, set the foreign key property 
    ' to the ID of the principal. 
    Dim newStudentGrade = New StudentGrade With
    {
        .EnrollmentID = 0,
        .Grade = CDec(4.0),
        .CourseID = 4022,
        .StudentID = 17
    }

    ' Adding the new object to the context will synchronize 
    ' the references with the foreign keys on the newStudentGrade object. 
    context.StudentGrades.AddObject(newStudentGrade)

    ' You can access Course and Student objects on the newStudentGrade object
    ' without loading the references explicitly because
    ' the lazy loading option is set to true in the constructor of SchoolEntities.
    Console.WriteLine("Student ID {0}:", newStudentGrade.Person.PersonID)
    Console.WriteLine("Course ID {0}:", newStudentGrade.Course.CourseID)

    context.SaveChanges()
End Using

' The following example creates a new StudentGrade and associates 
' the StudentGrade with the Course and Person by 
' setting the navigation properties to the Course and Person objects that were returned 
' by the query. 
' You do not need to call AddObject() in order to add the grade object 
' to the context, because when you assign the reference 
' to the navigation property the objects on both ends get synchronized by the Entity Framework. 
' Note, that the Entity Framework will not synchronize the ends untill the SaveChanges method 
' is called if your objects do not meet the change tracking requirements. 
Using context = New SchoolEntities()
    Dim courseID = 4022
    Dim course = (From c In context.Courses
                 Where c.CourseID = courseID
                 Select c).First()

    Dim personID = 17
    Dim student = (From p In context.People
                  Where p.PersonID = personID
                  Select p).First()

    ' The database will generate the EnrollmentID. 
    ' Use the navigation properties to create the association between the objects. 
    Dim newStudentGrade = New StudentGrade With
    {
        .EnrollmentID = 0,
        .Grade = CDec(4.0),
        .Course = course,
        .Person = student
    }
    context.SaveChanges()
End Using
// The following example creates a new StudentGrade object and associates
// the StudentGrade with the Course and Person by
// setting the foreign key properties. 

using (SchoolEntities context = new SchoolEntities())
{
    StudentGrade newStudentGrade = new StudentGrade
    {
        // The database will generate the EnrollmentID.
        EnrollmentID = 0,
        Grade = 4.0M,
        // To create the association between the Course and StudentGrade, 
        // and the Student and the StudentGrade, set the foreign key property 
        // to the ID of the principal.
        CourseID = 4022,
        StudentID = 17,
    };

    // Adding the new object to the context will synchronize
    // the references with the foreign keys on the newStudentGrade object.
    context.StudentGrades.AddObject(newStudentGrade);

    // You can access Course and Student objects on the newStudentGrade object
    // without loading the references explicitly because
    // the lazy loading option is set to true in the constructor of SchoolEntities.
    Console.WriteLine("Student ID {0}:", newStudentGrade.Person.PersonID);
    Console.WriteLine("Course ID {0}:", newStudentGrade.Course.CourseID);
    
    context.SaveChanges();
}

// The following example creates a new StudentGrade and associates
// the StudentGrade with the Course and Person by
// setting the navigation properties to the Course and Person objects that were returned
// by the query. 
// You do not need to call AddObject() in order to add the grade object
// to the context, because when you assign the reference 
// to the navigation property the objects on both ends get synchronized by the Entity Framework.
// Note, that the Entity Framework will not synchronize the ends untill the SaveChanges method
// is called if your objects do not meet the change tracking requirements. 
using (var context = new SchoolEntities())
{
    int courseID = 4022;
    var course = (from c in context.Courses
                 where c.CourseID == courseID
                 select c).First();

    int personID = 17;
    var student = (from p in context.People
                  where p.PersonID == personID
                  select p).First();

    StudentGrade grade = new StudentGrade
    {
        // The database will generate the EnrollmentID.
        Grade = 4.0M,
        // Use the navigation properties to create the association between the objects.
        Course = course,
        Person = student
    };
    context.SaveChanges();
}

상태 수정

외래 키 연결에서 관계를 변경하면 Unchanged 상태인 종속 개체의 상태가 Modified로 변경됩니다.

독립적 관계에서는 관계를 변경해도 종속 개체의 상태가 업데이트되지 않습니다.

동시성 관리

외래 키 연결과 독립 연결 모두에서 동시성 검사는 엔터티 키와 개념적 계층에서 ConcurrencyMode 특성을 fixed로 설정하여 정의된 다른 엔터티 속성을 기반으로 합니다.

그러나 독립 연결에서 관계 동시성 검사는 항상 관계의 양쪽 끝에 있는 개체에 대해 수행됩니다. 이 검사에서는 관련된 끝의 원래 키 값을 확인합니다. 개체가 개체 컨텍스트에서 분리된 상태에서 관계를 수정할 경우 원래 키 값을 다시 쿼리하거나 가져와 원래 관계를 다시 만들고, 개체를 개체 컨텍스트에 연결한 다음, 해당 개체 컨텍스트에서 관계를 적절하게 변경해야 합니다.

겹치는 키 사용

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

관련 개체 로드

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

// Get the order where currently AddressID = 1.  SalesOrderHeader order = context.SalesOrderHeaders.First(o=>o.SalesOrderID == orderId);

// Use BillToAddressID foreign key property 
// to change the association.  order.BillToAddressID = 2
order.AddressReference.Load();  

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

식별 및 비식별 관계에 대한 고려 사항

주 엔터티 기본 키가 종속 엔터티 기본 키의 일부이기도 한 경우 이 관계를 식별 관계라고 합니다. 식별 관계에서는 주 엔터티가 있어야만 종속 엔터티가 있을 수 있습니다. 이 제약 조건으로 인해 식별 관계에서의 동작은 다음과 같이 이루어집니다.

  • 주 개체를 삭제하면 종속 개체도 삭제됩니다. 이 동작은 관계에 대한 모델에서 <OnDelete Action="Cascade" />를 지정할 때와 동일합니다.

  • 관계를 제거하면 종속 개체가 삭제됩니다. EntityCollection 에서 Remove 메서드를 호출하면 해당 관계와 종속 개체가 모두 삭제 대상으로 표시됩니다.

  • 새 종속 개체를 만들려면 SaveChanges를 호출하기 전에 먼저 주 개체가 개체 컨텍스트나 데이터 소스에 있어야 합니다. 주 개체가 없으면 InvalidOperationException이 발생합니다.

비식별 관계에서 모델이 외래 키 연결을 기반으로 하는 경우 주 개체를 삭제하면 종속 개체의 외래 키가 null로 설정됩니다(null을 허용하는 키인 경우). 주 개체가 있어야만 존재할 수 있는 종속 개체의 경우에는 종속 개체를 수동으로 삭제하거나 종속 개체에 새로운 주 개체를 할당해야 합니다. 또는 모델에서 <OnDelete Action="Cascade" />를 지정하여 관련된 주 개체를 삭제할 때 종속 개체도 삭제되도록 할 수 있습니다.

단원 내용

탐색 속성

방법: 외래 키 속성을 사용하여 개체 간 관계 변경

방법: EntityReference를 사용하여 개체 간 관계 변경(Entity Framework)

방법: POCO 엔터티 간 관계 변경(Entity Framework)

참고 항목

작업

방법: EntityReference를 사용하여 개체 간 관계 변경(Entity Framework)
방법: 외래 키 속성을 사용하여 개체 간 관계 변경

개념

개체 만들기, 추가, 수정 및 삭제(Entity Framework)
POCO 엔터티 사용(Entity Framework)