共用方式為


關聯性中的外鍵和主體索引鍵

所有 一對一一對多 關聯性都是由參考主體端主要或替代索引鍵之相依端的外鍵所定義。 為了方便起見,此主要或替代密鑰稱為關聯性的「主體金鑰」。 多對多 關聯性是由兩個一對多關聯性所組成,每個關聯性都是由參考主體索引鍵的外鍵所定義。

提示

您可以在 ForeignAndPrincipalKeys.cs 中找到下列程式代碼。

外部索引鍵

組成外鍵的屬性或屬性通常 由慣例探索。 您也可以使用 對應屬性 或在 HasForeignKey 模型建置 API 中明確設定屬性。 HasForeignKey 可以搭配 Lambda 運算式使用。 例如,針對由單一屬性組成的外鍵:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.ContainingBlogId);
}

或者,針對由多個屬性組成的複合外鍵:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey(e => new { e.ContainingBlogId1, e.ContainingBlogId2 });
}

提示

在模型建置 API 中使用 Lambda 運算式可確保屬性使用可用於程式代碼分析和重構,也為 API 提供屬性類型,以供進一步鏈結的方法使用。

HasForeignKey 也可以傳遞外鍵屬性的名稱做為字串。 例如,針對單一屬性:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey("ContainingBlogId");
}

或者,針對複合外鍵:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey("ContainingBlogId1", "ContainingBlogId2");
}

使用字串在:

  • 屬性或屬性是私用的。
  • 屬性或屬性不存在於實體類型上,應該建立為 陰影屬性
  • 屬性名稱是根據模型建置程式的一些輸入來計算或建構。

不可為 Null 的外鍵數據行

如選擇性和必要關聯性中所述,外鍵屬性的可為 Null 性會決定關聯性是選擇性還是必要。 不過,可為 Null 的外鍵屬性可用於使用 [Required] 屬性的必要關聯性,或在模型建置 API 中呼叫 IsRequired 。 例如:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.BlogId)
        .IsRequired();
}

或者,如果依照慣例探索外鍵IsRequired則可以在沒有呼叫 HasForeignKey的情況下使用 :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .IsRequired();
}

最後的結果是,即使外鍵屬性可為 Null,資料庫中的外鍵數據行仍不可為 Null。 您可以視需要明確設定外鍵屬性本身來達成相同的動作。 例如:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .Property(e => e.BlogId)
        .IsRequired();
}

陰影外鍵

外鍵屬性可以建立為 陰影屬性。 陰影屬性存在於 EF 模型中,但不存在於 .NET 類型上。 EF 會持續追蹤內部的屬性值和狀態。

當想要從應用程式程式代碼/商業規則所使用的領域模型隱藏外鍵的關係概念時,通常會使用陰影外鍵。 然後,此應用程式程式代碼會透過導覽完全 操作關聯性

提示

如果要串行化實體,例如透過網路傳送,則當實體不在物件/圖形窗體中時,外鍵值可能會是保持關聯性資訊完好無損的實用方式。 因此,為了達到此目的,將外鍵屬性保留在 .NET 類型中通常是務實的。 外鍵屬性可以是私用的,這通常是很好的妥協,以避免公開外鍵,同時允許其值與實體一起移動。

陰影外鍵屬性通常是 由慣例所建立。 如果的 HasForeignKey 自變數不符合任何 .NET 屬性,也會建立陰影外鍵。 例如:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey("MyBlogId");
}

依照慣例,陰影外鍵會從關聯性中的主體索引鍵取得其類型。 除非偵測到關聯性或設定為必要,否則此類型是可為 Null 的。

您也可以明確建立陰影外鍵屬性,這對於設定屬性的 Facet 很有用。 例如,若要使 屬性不可為 Null:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .Property<string>("MyBlogId")
        .IsRequired();

    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey("MyBlogId");
}

提示

依照慣例,外鍵屬性會繼承關聯性中主體索引鍵的最大長度和 Unicode 支援等 Facet。 因此,很少需要在外鍵屬性上明確設定Facet。

如果指定的名稱不符合實體類型的任何屬性,則可以使用 ConfigureWarnings停用陰影屬性的建立。 例如:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.ConfigureWarnings(b => b.Throw(CoreEventId.ShadowPropertyCreated));

外鍵條件約束名稱

依照慣例,外鍵條件約束會命名為 FK_<dependent type name>_<principal type name>_<foreign key property name>。 針對複合外鍵, <foreign key property name> 會變成外鍵屬性名稱的底線分隔清單。

這可以在使用的模型建置 API HasConstraintName中變更。 例如:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasForeignKey(e => e.BlogId)
        .HasConstraintName("My_BlogId_Constraint");
}

提示

EF 運行時間不會使用條件約束名稱。 只有在使用 EF Core 移轉建立資料庫架構時,才會使用它。

外鍵的索引

根據慣例,EF 會為外鍵的屬性或屬性建立資料庫索引。 如需慣例所建立之索引類型的詳細資訊,請參閱 模型建置慣例

提示

關聯性定義於該模型中所含實體類型之間的EF模型中。 某些關聯性可能需要參考不同內容模型中的實體類型,例如,使用 BoundedContext 模式時。 在這些情況下,外鍵數據行應該對應至一般屬性,然後可以手動操作這些屬性來處理關聯性的變更。

主體金鑰

根據慣例,外鍵會限制在關聯性主體結尾的主鍵。 不過,可以改用替代索引鍵。 這會在 HasPrincipalKey 模型建置 API 上使用 來達成。 例如,針對單一屬性外鍵:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasPrincipalKey(e => e.AlternateId);
}

或者,針對具有多個屬性的複合外鍵:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasPrincipalKey(e => new { e.AlternateId1, e.AlternateId2 });
}

HasPrincipalKey 也可以傳遞替代索引鍵屬性的名稱做為字串。 例如,針對單一屬性索引鍵:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasPrincipalKey("AlternateId");
}

或者,針對複合索引鍵:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasMany(e => e.Posts)
        .WithOne(e => e.Blog)
        .HasPrincipalKey("AlternateId1", "AlternateId2");
}

注意

主體和外鍵中屬性的順序必須相符。 這也是在資料庫架構中定義索引鍵的順序。 它不一定與實體類型或數據表中的數據行中屬性的順序相同。

不需要呼叫 HasAlternateKey 在主體實體上定義替代索引鍵;當與非主鍵屬性的屬性搭配使用時 HasPrincipalKey ,就會自動完成此動作。 不過, HasAlternateKey 可用於進一步設定替代索引鍵,例如設定其資料庫條件約束名稱。 如需詳細資訊,請參閱 密鑰

與無索引鍵實體的關聯性

每個關聯性都必須有參考主體(主要或替代)索引鍵的外鍵。 這表示 無索引鍵實體類型 無法做為關聯性的主體結尾,因為沒有要參考之外鍵的主體索引鍵。

提示

實體類型不能有替代索引鍵,但沒有主鍵。 在此情況下,必須將替代索引鍵 (或其中一個替代索引鍵,如果有數個)升級為主鍵。

不過,無索引鍵實體類型仍然可以定義外鍵,因此可以做為關聯性的相依端。 例如,請考慮這些類型,其中 Tag 沒有索引鍵:

public class Tag
{
    public string Text { get; set; } = null!;
    public int PostId { get; set; }
    public Post Post { get; set; } = null!;
}

public class Post
{
    public int Id { get; set; }
}

Tag 可以在關聯性的相依端設定:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Tag>()
        .HasNoKey();

    modelBuilder.Entity<Post>()
        .HasMany<Tag>()
        .WithOne(e => e.Post);
}

注意

EF 不支援指向無索引鍵實體類型的導覽。 請參閱 GitHub 問題 #30331

多對多關聯性的外鍵

多對多關聯性中,外鍵是在聯結實體類型上定義,並對應至聯結數據表中的外鍵條件約束。 上述所有專案也可以套用至這些聯結實體外鍵。 例如,設定資料庫條件約束名稱:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasMany(e => e.Tags)
        .WithMany(e => e.Posts)
        .UsingEntity(
            l => l.HasOne(typeof(Tag)).WithMany().HasConstraintName("TagForeignKey_Constraint"),
            r => r.HasOne(typeof(Post)).WithMany().HasConstraintName("PostForeignKey_Constraint"));
}