關聯性、導覽屬性和外鍵
本文概述 Entity Framework 如何管理實體之間的關聯性。 它也提供如何對應及操作關聯性的一些指引。
EF 中的關聯性
在關係資料庫中,資料表之間的關聯性(也稱為關聯)是透過外鍵來定義。 外鍵 (FK) 是資料行或資料行的組合,用來建立及強制執行兩個數據表中的資料之間的連結。 一般有三種類型的關聯性:一對一、一對多和多對多。 在一對多關聯性中,外鍵定義于代表關聯性之多端的資料表上。 多對多關聯性牽涉到定義第三個數據表(稱為連接點或聯結資料表),其主鍵是由這兩個相關資料表的外鍵所組成。 在一對一關聯性中,主鍵會額外作為外鍵,而且任一資料表沒有個別的外鍵資料行。
下圖顯示兩個參與一對多關聯性的資料表。 Course 資料表是相依資料表,因為它包含 連結至 Department 資料表的 DepartmentID 資料行。
在 Entity Framework 中,實體可以透過關聯或關聯性與其他實體相關。 每個關聯性都包含兩個端點,描述該關聯性中兩個實體的實體類型和型別的乘數(一、零或一或多個)。 關聯性可能受引用條件約束所控管,該條件約束描述關聯性中的哪一個結尾是主體角色,而後者是相依角色。
導覽屬性提供在兩個實體類型之間巡覽關聯的方式。 每個物件都可以針對它所參與的每項關聯性擁有導覽屬性。 導覽屬性可讓您在雙向巡覽和管理關聯性,傳回參考物件(如果多重性是一或零或一個)或集合(如果多重性很多)。 您也可以選擇有單向導覽,在此情況下,您只會在參與關聯性的其中一個類型上定義導覽屬性,而不是在這兩種類型上定義。
建議在對應至資料庫中外鍵的模型中包含屬性。 包括了外部索引鍵之後,您便可以透過修改相依物件上的外部索引鍵值來建立或變更關聯性。 這個類型的關聯稱為外部索引鍵關聯。 使用中斷連線的實體時,使用外鍵更為重要。 請注意,使用 1 對 1 或 1 對 0 時。1 關聯性,沒有個別的外鍵資料行,主鍵屬性會做為外鍵,而且一律包含在模型中。
當模型中未包含外鍵資料行時,關聯資訊會以獨立物件的形式進行管理。 關聯性是透過物件參考來追蹤,而不是外鍵屬性。 這種類型的關聯稱為 獨立關聯 。 修改 獨立關聯 最常見的方法是修改針對參與關聯之每個實體所產生的導覽屬性。
您可以選擇在模型中使用一種或兩種關聯類型。 不過,如果您有純多對多關聯性,且聯結資料表只包含外鍵,EF 會使用獨立關聯來管理這類多對多關聯性。
下圖顯示以 Entity Framework 設計工具建立的概念模型。 此模型包含兩個參與一對多關聯性的實體。 這兩個實體都有導覽屬性。 Course 是相依實體,且已 定義 DepartmentID 外鍵屬性。
下列程式碼片段顯示使用 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; }
}
設定或對應關聯性
此頁面的其餘部分涵蓋如何使用關聯性來存取及運算元據。 如需在模型中設定關聯性的資訊,請參閱下列頁面。
- 若要在 Code First 中設定關聯性,請參閱 資料批註 和 Fluent API – 關聯性 。
- 若要使用 Entity Framework 設計工具設定關聯性,請參閱 與 EF 設計工具 的關聯性。
建立和修改關聯性
在外鍵關聯 中,當您變更關聯性時,具有 EntityState.Unchanged
狀態的相依物件狀態會變更為 EntityState.Modified
。 在獨立的關聯性中,變更關聯性並不會更新相依物件的狀態。
下列範例示範如何使用外鍵屬性和導覽屬性來關聯相關物件。 使用外鍵關聯,您可以使用任一方法來變更、建立或修改關聯性。 使用獨立關聯,則無法使用外部索引鍵屬性。
將新值指派給外鍵屬性,如下列範例所示。
course.DepartmentID = newCourse.DepartmentID;
下列程式碼會藉由將外鍵設定為 null 來移除關聯性。 請注意,外鍵屬性必須是可為 Null 的。
course.DepartmentID = null;
注意
如果參考處於新增狀態(在此範例中為 course 物件),在呼叫 SaveChanges 之前,參考導覽屬性將不會與新物件的索引鍵值同步處理。 不會同步化是因為物件內容未包含新加入之物件的永久索引鍵,除非先儲存這些索引鍵。 如果您在設定關聯性時必須立即完全同步處理新的物件,請使用下列其中一種方法。*
將新物件指派給導覽屬性。 下列程式碼會建立課程與
department
之間的關聯性。 如果物件附加至內容,course
也會將 加入集合department.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;
在實體集合中刪除或加入物件。 例如,您可以將 型別
Course
的物件加入至department.Courses
集合。 此作業會建立特定 課程 與特定department
之間的關聯性。 如果物件附加至內容,則 course 物件上的 部門參考和外鍵屬性將會設定為適當的department
。department.Courses.Add(newCourse);
使用
ChangeRelationshipState
方法來變更兩個實體物件之間指定關聯性的狀態。 使用 N 層應用程式和 獨立關聯 時,最常使用此方法(無法與外鍵關聯搭配使用)。 此外,若要使用此方法,您必須下拉至ObjectContext
,如下列範例所示。
在下列範例中,Instructors 與 Courses 之間有多對多關聯性。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 會自動管理具有 Proxy 之 POCO 實體的這項同步處理(也稱為關聯性修正)。 如需詳細資訊,請參閱 使用 Proxy 。
如果您使用不含 Proxy 的 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
- 針對 執行 LINQ 查詢
DbSet
載入相關物件
在 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 Designer 建立模型時,將 屬性設定為 固定 , ConcurrencyMode
以指定應該檢查屬性是否為並行。 使用 Code First 來定義模型時,請在 ConcurrencyCheck
您想要檢查的並行屬性上使用注釋。 使用 Code First 時,您也可以使用 TimeStamp
批註來指定應該檢查屬性是否為並行。 在指定的類別中,您只能有一個 timestamp 屬性。 Code First 會將此屬性對應至資料庫中不可為 Null 的欄位。
建議您在處理參與並行檢查和解析的實體時,一律使用外鍵關聯。
如需詳細資訊,請參閱 處理並行衝突 。
使用重迭的索引鍵
重疊索引鍵是複合索引鍵,其索引鍵中的部分屬性也是實體中另一個索引鍵的一部分。 在獨立關聯中不能有重疊索引鍵。 若要變更包括重疊索引鍵的外部索引鍵關聯,我們建議您修改外部索引鍵值,而不是使用物件參考。