Share via


外部キーの使用 (Entity Framework)

.NET Framework バージョン 4 以降では、エンティティ型の外部キー プロパティを公開し、外部キーからリレーションシップを定義できるようになりました。 Entity Data Model ウィザードの [モデルに外部キー列を含める] オプションは既定で選択されています。 このオプションを選択すると、生成されたエンティティ オブジェクトには、外部キー列にマッピングするスカラー プロパティが含まれています。

外部キー プロパティを使用すると、プリンシパル オブジェクトをクエリせずにリレーションシップを作成したり変更したりできます。 外部キー プロパティを含めた場合も、依存のプリンシパル オブジェクトへの参照を更新するか、依存オブジェクトをプリンシパル オブジェクトのコレクションに追加すると、リレーションシップを変更できます。 エンティティの参照とコレクションにアクセスする一般的な方法は、ナビゲーション プロパティからです。 詳細については、「ナビゲーション プロパティ」を参照してください。

Entity Framework は、参照、コレクション、および外部キー プロパティを同期しようとします。 依存オブジェクトの外部キー プロパティを更新した場合、そのオブジェクトの参照とプリンシパル オブジェクトのコレクションを同期する必要があります。 この同期を発生させるには、エンティティをオブジェクトのコンテキストにアタッチし、Entity Framework でエンティティを追跡する必要があります。 詳細については、「ID 解決、状態管理、および変更の追跡 (Entity Framework)」および「リレーションシップの定義と管理 (Entity Framework)」を参照してください。

このトピックの例には、School モデル が使用されています。 これらの例のエンティティ型は Entity Data Model ツールによって生成されます。 生成された型は Entity Framework によって追跡されます。 POCO 型を使用している場合は、「POCO エンティティでの変更の追跡 (Entity Framework)」を参照してください。

次の例は、2 個の既存のオブジェクト間のリレーションシップを変更する方法を示しています。

Dim studentId As Integer = 6
Dim enrollmentID As Integer = 2

Using context = New SchoolEntities()
    ' Get StudentGrade. 
    Dim grade = (From g In context.StudentGrades
                 Where g.EnrollmentID = enrollmentID
                 Select g).First()

    ' Change the relationship. 
    grade.StudentID = studentId
    ' You can access Person reference object on the grade object 
    ' without loading the reference explicitly when 
    ' the lazy loading option is set to true. 
    Console.WriteLine(grade.Person.PersonID)
    ' Save the changes. 
    context.SaveChanges()
End Using
int studentId = 6;
int enrollmentID = 2;

using (var context = new SchoolEntities())
{
    // Get StudentGrade.
    var grade = (from g in context.StudentGrades
                 where g.EnrollmentID == enrollmentID
                 select g).First();

    // Change the relationship.
    grade.StudentID = studentId;
    // You can access Person reference object on the grade object
    // without loading the reference explicitly when
    // the lazy loading option is set to true.
    Console.WriteLine(grade.Person.PersonID);
    // Save the changes.
    context.SaveChanges();
}

次の例は、外部キー プロパティを設定して、新しい依存オブジェクトと既存のプリンシパル オブジェクト間のリレーションシップを設定する方法を示しています。

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

次の例は、新しい依存と新しいプリンシパル オブジェクト間のリレーションシップを設定する方法を示しています。

Using context = New SchoolEntities()
    ' The database will generate PersonID. 
    ' The object context will get the ID 
    ' After the SaveChanges is called. 
    Dim newStudent = New Person With
    {
        .PersonID = 0,
        .LastName = "Li",
        .FirstName = "Yan"
     }
    ' The database will generate EnrollmentID. 
    ' The object context will get the ID 
    ' After the SaveChanges is called. 
    Dim newStudentGrade = New StudentGrade With
    {
        .EnrollmentID = 0,
        .Grade = CDec(4.0),
        .StudentID = 50
    }

    ' Add newStudent to object context. 
    ' The newStudent's state will change from Detached to Added. 
    context.People.AddObject(newStudent)

    ' To associate the new objects you can do one of the following: 
    ' Add the new dependent object to the principal object: newStudent.StudentGrades.Add(newStudentGrade). 
    ' Assign the reference (principal) object to the navigation property of the 
    ' dependent object: newStudentGrade.Person = newStudent. 
    ' Both of these methods will synchronize the navigation properties on both ends of the relationship. 

    ' Adding the newStudentGrade to newStudent will change newStudentGrade's status 
    ' from Detached to Added. 
    newStudent.StudentGrades.Add(newStudentGrade)
    ' Navigation properties in both directions will work immediately. 
    Console.WriteLine("Access StudentGrades navigation property to get the count: ", _
                      newStudent.StudentGrades.Count)
    Console.WriteLine("Access Person navigation property: {0} ", _
                      newStudentGrade.Person.FirstName)

    context.SaveChanges()
End Using
using (var context = new SchoolEntities())
{
    Person newStudent = new Person
    {
        // The database will generate PersonID.
        // The object context will get the ID 
        // After the SaveChanges is called.
        PersonID = 0,
        LastName = "Li",
        FirstName = "Yan"
    };
    StudentGrade newStudentGrade = new StudentGrade
    {
        // The database will generate EnrollmentID.
        // The object context will get the ID 
        // After the SaveChanges is called.
        EnrollmentID = 0,
        Grade = 4.0M,
        StudentID = 50
    };

    // Add newStudent to object context. 
    // The newStudent's state will change from Detached to Added.
    context.People.AddObject(newStudent);

    // To associate the new objects you can do one of the following:
    // Add the new dependent object to the principal object: newStudent.StudentGrades.Add(newStudentGrade).
    // Assign the reference (principal) object to the navigation property of the 
    // dependent object: newStudentGrade.Person = newStudent.
    // Both of these methods will synchronize the navigation properties on both ends of the relationship.

    // Adding the newStudentGrade to newStudent will change newStudentGrade's status
    // from Detached to Added.
    newStudent.StudentGrades.Add(newStudentGrade);
    // Navigation properties in both directions will work immediately.
    Console.WriteLine("Access StudentGrades navigation property to get the count: ",
        newStudent.StudentGrades.Count);
    Console.WriteLine("Access Person navigation property: {0} ", newStudentGrade.Person.FirstName);

    context.SaveChanges();
}

外部キー アソシエーションでは、Entity Framework 4 より前のバージョンのように、参照を使用してリレーションシップを設定することもできます。

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

以下は、クライアントとサービスとの単純な通信を示しています。クライアントはサービスからオブジェクトを要求し、そのオブジェクトを更新すると、サービスを呼び出してデータベースに変更を保存します。

サービスは 2 つのメソッドを定義します。

Private Shared Function GetOriginalValue(ByVal ID As Integer) As StudentGrade
    Dim originalItem As StudentGrade
    Using context As New SchoolEntities()
        originalItem = context.StudentGrades.Where(Function(g) g.EnrollmentID = ID).FirstOrDefault()

        context.Detach(originalItem)
    End Using
    Return originalItem
End Function

Private Shared Sub SaveUpdates(ByVal updatedItem As StudentGrade)
    Using context As New SchoolEntities()
        ' Query for the StudentGrade object with the specified ID. 
        Dim original = (From o In context.StudentGrades
            Where o.EnrollmentID = updatedItem.EnrollmentID
            Select o).First()

        ' Apply changes. 
        context.StudentGrades.ApplyCurrentValues(updatedItem)

        ' Save changes. 
        context.SaveChanges()
    End Using
End Sub
static private StudentGrade GetOriginalValue(int ID)
{
    StudentGrade originalItem;
    using (SchoolEntities context
        = new SchoolEntities())
    {
        originalItem =
            context.StudentGrades.Where(g => g.EnrollmentID == ID).FirstOrDefault();

        context.Detach(originalItem);
    }
    return originalItem;
}

static private void SaveUpdates(StudentGrade updatedItem)
{
    using (SchoolEntities context
        = new SchoolEntities())
    {
        // Query for the StudentGrade object with the specified ID.
        var original = (from o in context.StudentGrades
                         where o.EnrollmentID == updatedItem.EnrollmentID
                         select o).First();

        // Apply changes.
        context.StudentGrades.ApplyCurrentValues(updatedItem);

        // Save changes.
        context.SaveChanges();
    }
}

クライアントは外部キー プロパティの値を更新し、更新されたオブジェクトをサービスに送信します。

' A client calls a service to get the original object. 
Dim studentGrade As StudentGrade = GetOriginalValue(3)
' Change the relationships. 
studentGrade.CourseID = 5
studentGrade.StudentID = 10
' The client calls a method on a service to save the updates. 
// A client calls a service to get the original object.
StudentGrade studentGrade = GetOriginalValue(3);
// Change the relationships.
studentGrade.CourseID = 5;
studentGrade.StudentID = 10;
// The client calls a method on a service to save the updates. 
SaveUpdates(studentGrade);

参照

概念

リレーションシップの定義と管理 (Entity Framework)
POCO エンティティの使用 (Entity Framework)