ここにすべて載ってます。
以上
・・・
だとさすがにあれなので、説明します
複合外部キーの書き方(概要)
複合キーなので、以下のことをする必要があります。
- DbContextクラスOnModelCreating時に、modelBuilderでキーの組み合わせを設定する。
- ※ナビゲーションプロパティを記述する方法と省略する方法がある。
以下、実際にコードを見ながら、詳細にみていきます。
例、ブログと投稿記事
まず、以下の関係をよく頭に入れてください。
Blog:ブログ
Post:投稿記事
- ブログA
- 投稿1
- 投稿2
- 投稿3
- ブログB
- 投稿1
- 投稿2
- …
それでは、実際にコードを見ていきましょう。
例1. ナビゲーションプロパティとmodelBuilderの両方を記述したパターン
以下の二つの手順で実現できます。
- Step1:ナビゲーションプロパティの記述
- Step2:modelBuilderで、複合外部キーの設定
Step1.ナビゲーションプロパティを記述する
//ブログ(テーブル定義) public class Blog { //例、BlogIdとUrlで、投稿記事を制約する public int BlogId { get; set; } public string Url { get; set; } //ナビゲーションプロパティ(Navigation Property) public List<Post> Posts { get; set; } //ブログは投稿記事のリストを持つ } //投稿記事(テーブル定義) public class Post { public int PostId { get; set; } //暗黙的なPrimaryKey public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } //外部キーを用意 public string Url { get; set; } //外部キーを用意 //ナビゲーションプロパティ(Navigation Property) public Blog Blog { get; set; } //投稿記事はブログに所属する。 }
Step2.modelBuilderで、複合外部キーの設定を行う
class CompositeForeignContext : DbContext { //テーブルのキー設定など記述する protected override void OnModelCreating(ModelBuilder modelBuilder) { //Blogの設定 modelBuilder.Entity<Blog>() .HasKey(blog => new { blog.BlogId, blog.Url }); //複合PrimaryKeyの設定 //Postの設定 modelBuilder.Entity<Post>() .HasOne(post => post.Blog) //Blogを参照する。 .WithMany(blog => blog.Posts) //Blogに対し、Postは複数存在する。 .HasForeignKey(post => new { post.BlogId, post.Url }); //外部制約キーの指定 //(暗黙的にBlogのPrimaryKeyが外部キーとなる) } }
以上です。
例1.まとめ
まとめるとソースコードは以下のようになります。
using Microsoft.EntityFrameworkCore; using System.Collections.Generic; class CompositeForeignContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } //コンストラクタ public CompositeForeignContext(DbContextOptions<CompositeForeignContext> options) : base(options) { } //ブログ(テーブル定義) public class Blog { //例、BlogIdとUrlで、投稿記事を制約する public int BlogId { get; set; } public string Url { get; set; } //ナビゲーションプロパティ(Navigation Property) public List<Post> Posts { get; set; } //ブログは投稿記事のリストを持つ } //投稿記事(テーブル定義) public class Post { public int PostId { get; set; } //暗黙的なPrimaryKey public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } //外部キーを用意 public string Url { get; set; } //外部キーを用意 //ナビゲーションプロパティ(Navigation Property) public Blog Blog { get; set; } //投稿記事はブログに所属する。 } //テーブルのキー設定など記述する protected override void OnModelCreating(ModelBuilder modelBuilder) { //Blogの設定 modelBuilder.Entity<Blog>() .HasKey(blog => new { blog.BlogId, blog.Url }); //複合PrimaryKeyの設定 //Postの設定 modelBuilder.Entity<Post>() //Postの設定 .HasOne(post => post.Blog) //Blogを参照する。 .WithMany(blog => blog.Posts) //Blogに対し、Postは複数存在する。 .HasForeignKey(post => new { post.BlogId, post.Url }); //外部制約キーの指定。 //(暗黙的にBlogのPrimaryKeyが外部キーとなる) } }
例2.ナビゲーションプロパティを省略したパターン
ナビゲーションプロパティの記述は実は省略できます。
modelBuilderで、複合外部キーの設定だけを行います。
class CompositeForeignNoNavContext : DbContext { //テーブルのキー設定など記述する protected override void OnModelCreating(ModelBuilder modelBuilder) { //Blogの設定 modelBuilder.Entity<Blog>() .HasKey(blog => new { blog.BlogId, blog.Url }); //複合PrimaryKeyの設定 //Postの設定 modelBuilder.Entity<Post>() .HasOne<Blog>() //Blogを参照する。 .WithMany() //Blogに対し、Postは複数存在する。 .HasForeignKey(post => new { post.BlogId, post.Url }); //複合外部キーの指定 //(暗黙的にBlogのPrimaryKeyが割り当てられる) } }
HasOne()とWithMany()の記述の仕方が、ナビゲーションプロパティありの場合と変わります。
ソース全体はこんな感じ
using Microsoft.EntityFrameworkCore; using System.Collections.Generic; class CompositeForeignNoNavContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } //コンストラクタ public CompositeForeignNoNavContext(DbContextOptions<CompositeForeignNoNavContext> options) : base(options) { } //ブログ(テーブル定義) public class Blog { //例、BlogIdとUrlで、投稿記事を制約する public int BlogId { get; set; } public string Url { get; set; } } //投稿記事(テーブル定義) public class Post { public int PostId { get; set; } //暗黙的なPrimaryKey public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } //外部キーを用意 public string Url { get; set; } //外部キーを用意 } //テーブルのキー設定など記述する protected override void OnModelCreating(ModelBuilder modelBuilder) { //Blogの設定 modelBuilder.Entity<Blog>() .HasKey(blog => new { blog.BlogId, blog.Url }); //複合PrimaryKeyの設定 //Postの設定 modelBuilder.Entity<Post>() .HasOne<Blog>() //Blogを参照する。 .WithMany() //Blogに対し、Postは複数存在する。 .HasForeignKey(post => new { post.BlogId, post.Url }); //複合外部キーの指定 //(暗黙的にBlogのPrimaryKeyが割り当てられる) } }
生成されるMigrationsファイルについて
「add-migration」を行うと以下のファイルが生成されました。
public partial class CompositeForeignContext : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Blogs", columns: table => new { BlogId = table.Column<int>(nullable: false), Url = table.Column<string>(nullable: false) }, constraints: table => { table.PrimaryKey("PK_Blogs", x => new { x.BlogId, x.Url }); }); migrationBuilder.CreateTable( name: "Posts", columns: table => new { PostId = table.Column<int>(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Title = table.Column<string>(nullable: true), Content = table.Column<string>(nullable: true), BlogId = table.Column<int>(nullable: false), Url = table.Column<string>(nullable: true) }, constraints: table => { table.PrimaryKey("PK_Posts", x => x.PostId); table.ForeignKey( name: "FK_Posts_Blogs_BlogId_Url", columns: x => new { x.BlogId, x.Url }, principalTable: "Blogs", principalColumns: new[] { "BlogId", "Url" }, onDelete: ReferentialAction.Restrict); }); migrationBuilder.CreateIndex( name: "IX_Posts_BlogId_Url", table: "Posts", columns: new[] { "BlogId", "Url" }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "Posts"); migrationBuilder.DropTable( name: "Blogs"); } }
ちゃんと複合外部キーが設定できているようです。
ちなみにナビゲーションプロパティの省略ありなしで、生成されるmigrationファイルを比較してみましたが、差はありませんでした。
コメント