定義及管理關聯性
在 Entity Framework 中,實體可以透過關聯來與其他實體產生聯結。 這個實體間的關聯性在概念模型中是透過 Association 項目來定義。 每個關聯性會包含兩端,描述實體類型和類型的多重性 (一個、零或一個,或很多個)。 關聯性可能由參考條件約束控制,它會描述關聯性中的哪一端是主要角色,哪一端是相依角色。
從 .NET Framework 4 版開始,您可以在概念模型中包括外部索引鍵。 實體資料模型 精靈中的 [在模型中包含外部索引鍵資料行] 選項預設為選取狀態。 選取此選項時,產生之實體物件便有對應至外部索引鍵資料行的純量屬性。 包括了外部索引鍵之後,您便可以透過修改相依物件上的外部索引鍵值來建立或變更關聯性。 這個類型的關聯稱為外部索引鍵關聯。
概念模型中若未包括外部索引鍵資料行,關聯資訊會當做獨立物件來管理。 關聯性是透過物件參考而非外部索引鍵屬性來追蹤,它們會以 ObjectStateManager 中的 ObjectStateEntry 物件來表示。這個類型的關聯稱為獨立關聯。 修改獨立關聯之最常用的方式是修改針對參與關聯之每個實體所產生的導覽屬性。
在這兩種關聯中,每個物件都可以針對它所參與的每項關聯性擁有導覽屬性。 導覽屬性可讓您巡覽及管理雙向的關聯性,如果多重性是一個或零或多個會傳回參考物件,或者如果多重性是很多個,則傳回物件集合。
混合關聯性
您可以選擇在模型中使用一種或兩種關聯類型。 但是,如果您在模型中包括一個只包含外部索引鍵的資料表 (也稱為純粹聯結資料表),則獨立關聯將用來管理純粹多對多關聯性,甚至如果您在模型中指定使用外部索引鍵關聯也一樣。 實體資料模型 精靈不會建立對應至純粹聯結資料表的實體。
建立和修改關聯性
在 Entity Framework 中,您可以透過多種方式建立和修改關聯性。
將新物件指派給導覽屬性。 下列程式碼會建立
order
和customer
之間的關聯性。 如果物件已附加至物件內容,order
已加入至customer.Orders
集合,則order
物件上的對應外部索引鍵屬性會設定為客戶的索引鍵屬性值:order.Customer = customer
在實體集合中刪除或加入物件。 例如,您可以使用 Add 方法來將 Order 類型的物件加入至
customer.Orders
集合。 這項作業會建立特別之order
和customer
之間的關聯性。 如果物件已附加至物件內容,order
物件上的客戶參考和外部索引鍵屬性將設定為適當的customer
:customer.Orders.Add(order)
在外部索引鍵關聯中,您可以將新值指派給外部索引鍵屬性,如下列範例所示。
注意: 如果參考在呼叫 SaveChanges 之前處於已加入的狀態,參考導覽屬性將不會與新物件的索引鍵值同步化。 不會同步化是因為物件內容未包含新加入之物件的永久索引鍵,除非先儲存這些索引鍵。 如需詳細資訊,請參閱使用實體索引鍵 (Entity Framework)。 如果您必須讓新物件在您設定關聯性時便完全同步化,請使用前兩個方法的其中一個。 order.CustomerID = newCustomer.CustomerID order.CustomerID = null
建立特定物件的實體索引鍵。 如果物件內容中已有物件使用此索引鍵,CreateEntityKey 方法將會傳回現有物件的 EntityKey。 基於與 .NET Framework 3.5 SP1 的回溯相容性,提供這個方法。
order.CustomerReference.EntityKey = ctx.CreateEntityKey("EntitySetName", newObject)
當您使用以上所述之其中一種方法來變更附加至物件內容之物件的關聯性時,Entity Framework 必須讓外部索引鍵、參考和集合同步化。 Entity Framework 將管理特定適合之物件類型的這項同步化作業:
本身是由 實體資料模型 工具產生之程式碼的實體物件。
會實作 IEntityWithChangeTracker 介面的自訂物件。
使用 Proxy 的 POCO 實體。 如需詳細資訊,請參閱建立 POCO Proxy 的需求 (Entity Framework)。
如果您使用沒有 Proxy 的 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" />
以確保在您刪除相關主要物件時也會刪除相依物件。
本節內容
HOW TO:使用 EntityReference 變更物件間的關聯性 (Entity Framework)
HOW TO:變更 POCO 實體之間的關聯性 (Entity Framework)
另請參閱
工作
HOW TO:使用 EntityReference 變更物件間的關聯性 (Entity Framework)
HOW TO:使用外部索引鍵屬性變更物件間的關聯性