關聯性導覽
EF Core 關聯性是由 外鍵所定義。 流覽會分層在外鍵上,以提供自然、面向對象檢視來讀取和操作關聯性。 藉由使用導覽,應用程式可以處理實體的圖形,而不需擔心外鍵值發生的情況。
重要
多個關聯性無法共享導覽。 任何外鍵最多都可以與一個從主體流覽至相依的導覽相關聯,而且最多只能與一個從相依至主體的瀏覽產生關聯。
參考導覽
導覽有兩種表單--參考和集合。 參考導覽是另一個實體的簡單對象參考。 它們代表一對多和一對一關係中的「一」端。 例如:
public Blog TheBlog { get; set; }
參考導覽必須有 setter,但不需要是公用的。 參考導覽不應自動初始化為非 Null 預設值;這樣做相當於判斷實體不存在時存在。
使用 C# 可為 Null 的參考型別時,選擇性關聯性必須可為 Null 的參考導覽:
public Blog? TheBlog { get; set; }
必要關聯性的參考導覽可以是可為 Null 或不可為 Null。
集合導覽
集合導覽是 .NET 集合類型的實例;也就是說,任何實作 ICollection<T>的類型。 集合包含相關實體類型的實例,其中可以有任意數目。 它們代表了一對多和多對多關係的“多”端。 例如:
public ICollection<Post> ThePosts { get; set; }
集合導覽不需要 setter。 初始化集合內嵌很常見,因此不需要檢查屬性是否為 null
。 例如:
public ICollection<Post> ThePosts { get; } = new List<Post>();
提示
請勿不小心建立表示式主體屬性,例如 public ICollection<Post> ThePosts => new List<Post>();
。 這會在每次存取屬性時建立新的空白集合實例,因此將無用做為導覽。
集合類型
基礎集合實例必須實 ICollection<T>作 ,而且必須有工作 Add
方法。 通常使用 List<T> 或 HashSet<T>。 List<T>
對於少量的相關實體而言,效率很高,而且會維持穩定的順序。 HashSet<T>
對於大量實體,具有更有效率的查閱,但沒有穩定的排序。 您也可以使用自己的自訂集合實作。
重要
集合必須使用參考相等。 建立 HashSet<T>
集合導覽的 時,請務必使用 ReferenceEqualityComparer。
陣列無法用於集合導覽,因為即使它們實 ICollection<T>
作 ,方法 Add
也會在呼叫時擲回例外狀況。
即使集合實例必須是 ICollection<T>
,集合也不需要公開為這類。 例如,通常會將瀏覽公開為 IEnumerable<T>,其提供應用程式程式代碼無法隨機修改的只讀檢視。 例如:
public class Blog
{
public int Id { get; set; }
public IEnumerable<Post> ThePosts { get; } = new List<Post>();
}
此模式的變化包括視需要操作集合的方法。 例如:
public class Blog
{
private readonly List<Post> _posts = new();
public int Id { get; set; }
public IEnumerable<Post> Posts => _posts;
public void AddPost(Post post) => _posts.Add(post);
}
應用程式程式代碼仍然可以將公開的集合 ICollection<T>
轉換成 ,然後加以操作。 如果這是一個考慮,則實體可能會傳回集合的防禦複本。 例如:
public class Blog
{
private readonly List<Post> _posts = new();
public int Id { get; set; }
public IEnumerable<Post> Posts => _posts.ToList();
public void AddPost(Post post) => _posts.Add(post);
}
請仔細考慮從這個 取得的值是否足夠高,超過每次存取導覽時建立集合複本的額外負荷。
提示
此最終模式的運作方式是,根據預設,EF 會透過其支援字段存取集合。 這表示 EF 本身會從實際集合新增和移除實體,而應用程式只會與集合的防禦性複本互動。
集合導覽的初始化
集合導覽可以立即由實體類型初始化:
public class Blog
{
public ICollection<Post> Posts { get; } = new List<Post>();
}
或者懶散:
public class Blog
{
private ICollection<Post>? _posts;
public ICollection<Post> Posts => _posts ??= new List<Post>();
}
例如,如果EF需要將實體新增至集合導覽,則在執行查詢時,如果目前 null
為 ,則會初始化集合。 建立的實例取決於流覽的公開類型。
- 如果導覽公開為
HashSet<T>
,則會建立using ReferenceEqualityComparer 的HashSet<T>
實例。 - 否則,如果導覽公開為具有無參數建構函式的具象類型,則會建立該具體類型的實例。 這也適用於
List<T>
,但也適用於其他集合類型,包括自定義集合類型。 - 否則,如果巡覽公開為
IEnumerable<T>
、、ICollection<T>
或ISet<T>
,則會建立usingReferenceEqualityComparer
的HashSet<T>
實例。 - 否則,如果導覽公開為
IList<T>
,則會建立的List<T>
實例。 - 否則,會擲回例外狀況。
注意
如果使用包含變更追蹤 Proxy 的通知實體,則會ObservableCollection<T>使用 和 ObservableHashSet<T> 來取代 List<T>
和 HashSet<T>
。
重要
如變更追蹤檔中所述,EF 只會追蹤具有指定索引鍵值之任何實體的單一實例。 這表示做為導覽的集合必須使用 參考相等語意。 不會覆寫物件相等的實體類型預設會取得此專案。 請務必在建立 HashSet<T>
以做為導覽的 時使用 ReferenceEqualityComparer ,以確保它適用於所有實體類型。
設定導覽
在設定關聯性時,模型中會包含導覽。 也就是說,依照 慣例或在 HasOne
模型建置 API 中使用 、 HasMany
等。 大部分與導覽相關聯的組態都是藉由設定關聯性本身來完成。
不過,有些組態類型專屬於導覽屬性本身,而不是屬於整體關聯性組態的一部分。 這種類型的組態是使用 方法完成的 Navigation
。 例如,若要強制 EF 透過其 屬性存取導覽,而不是使用支援欄位:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Navigation(e => e.Posts)
.UsePropertyAccessMode(PropertyAccessMode.Property);
modelBuilder.Entity<Post>()
.Navigation(e => e.Blog)
.UsePropertyAccessMode(PropertyAccessMode.Property);
}
注意
呼叫 Navigation
無法用來建立導覽屬性。 它只會用來設定先前透過定義關聯性或慣例所建立的導覽屬性。
必要的導覽
如果需要關聯性,則需要從相依主體巡覽,這又表示外鍵屬性不可為 Null。 相反地,如果外鍵可為 Null,則瀏覽是選擇性的,因此關聯性是選擇性的。
從主體到相依的參考導覽不同。 在大部分情況下,主體實體一律可以存在,而不需要任何相依實體。 也就是說,必要的關聯性並不表示一律會有至少一個相依實體。 EF 模型沒有任何方法,也沒有任何標準方式在關係資料庫中,以確保主體與特定數目的相依性相關聯。 如果需要,則必須在應用程式(商業)邏輯中實作它。
此規則有一個例外狀況-當主體和相依類型共用關係資料庫中的相同數據表,或包含在檔中時。 這可能發生於擁有的類型,或共用相同數據表的非擁有類型。 在此情況下,從主體到相依的導覽屬性可以標示為必要,表示相依項目必須存在。
使用 Navigation
方法完成巡覽屬性的設定。 例如:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Navigation(e => e.BlogHeader)
.IsRequired();
}