対一リレーションシップと一対多リレーションシップは、すべて主要側の主キーまたは代替キーを参照する従属側の外部キーによって定義されます。 便宜上、この主キーまたは代替キーは、リレーションシップの "プリンシパル キー" と呼ばれます。 多対多 リレーションシップは 2 つの一対多リレーションシップで構成され、それぞれがプリンシパル キーを参照する外部キーによって定義されます。
ヒント
次のコードは 、ForeignAndPrincipalKeys.csにあります。
外部キー
外部キーを構成するプロパティは、多くの場合、 規則によって検出されます。 プロパティは、 マッピング属性 を使用するか、モデル構築 API の HasForeignKey
を使用して明示的に構成することもできます。
HasForeignKey
はラムダ式と共に使用できます。 たとえば、1 つのプロパティで構成される外部キーの場合は、次のようになります。
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 でラムダ式を使用すると、プロパティの使用をコード分析とリファクタリングに使用できるようになり、さらにチェーンされたメソッドで使用するために API にプロパティ型も提供されます。
HasForeignKey
外部キー プロパティの名前を文字列として渡すこともできます。 たとえば、1 つのプロパティの場合:
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 許容になります。
シャドウ外部キー プロパティを明示的に作成することもできます。これは、プロパティのファセットを構成する場合に便利です。 たとえば、プロパティを 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 サポートなどのファセットを継承します。 したがって、外部キー プロパティに対してファセットを明示的に構成する必要はほとんどありません。
指定した名前がエンティティ型のプロパティと一致しない場合のシャドウ プロパティの作成は、 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>
は外部キー プロパティ名のアンダースコアで区切られたリストになります。
これは、 HasConstraintName
を使用してモデル構築 API で変更できます。 例えば次が挙げられます。
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 Migrations を使用してデータベース スキーマを作成する場合にのみ使用されます。
外部キーのインデックス
慣例として、EF は外部キーのプロパティに対してデータベースのインデックスを作成します。 規則によって作成されるインデックスの種類の詳細については、「 モデル構築規則 」を参照してください。
ヒント
リレーションシップは、そのモデルに含まれるエンティティ型間の EF モデルで定義されます。 一部のリレーションシップでは、 BoundedContext パターンを使用する場合など、別のコンテキストのモデルでエンティティ型を参照する必要がある場合があります。 このような状況では、外部キー列を通常のプロパティにマップする必要があり、これらのプロパティを手動で操作してリレーションシップへの変更を処理できます。
プリンシパル キー
規則により、外部キーは関係の主要側の主キーに結び付けられます。 ただし、代わりに代替キーを使用できます。 これは、モデル構築 API の HasPrincipalKey
を使用して実現されます。 たとえば、単一の属性外部キーの場合:
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
は、代替キー プロパティの名前を文字列として渡すこともできます。 たとえば、1 つのプロパティ キーの場合:
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"));
}
.NET