本記事はFIXERが提供する「cloud.config Tech Blog」に掲載された「Entity Framework Coreでリレーションシップを設定する方法」を再編集したものです。
こんにちは、あおいです。来世に生まれ変わるなら、外銀トレーダーとしてMAKE MONEYしたい!!! MDになりたい!!!(なれねーよ)
さて、Entity Framework Coreでリレーションシップを設定するとき、設定方法がよく分からず詰まってしまう方がいらっしゃると思います。
そこで、今回はEntity Framework Coreで1対1・1対多・多対多の各リレーションシップの設定方法を紹介したいと思います。
1対1
以下のBookテーブルとAuthorテーブルで、1冊の書籍に1人の著者が紐づいていると仮定します。まぁ、複数人の著者のケースもありますが、今回は一旦スルーとしておきます。
public class Book { public int Id { get; set; } public string Title { get; set; } public int Price { get; set; } } public class Author { public int Id { get; set; } public string Name { get; set; } }
まず、各テーブルにナビゲーションプロパティと外部キーを記述します。
public class Book { public int Id { get; set; } public string Title { get; set; } public int Price { get; set; } // ナビゲーションプロパティ public Author Author { get; set; } } public class Author { public int Id { get; set; } public string Name { get; set; } // 外部キー public int BookId { get; set; } // ナビゲーションプロパティ public Book Book { get; set; } }
次に、DbContext.csのOnModelCreatingで、FluentAPIを使ってリレーションシップを設定します。おそらく、こちらのFluentAPIの部分で混乱してしまう方がいらっしゃると思いますが、次のように置き換えて考えてみてください。
①modelBuilder.Entity<T>:
②HasOne(b => b.Author):Book(書籍)は1人の著者(Author)が存在し、
③WithOne(a => a.Book):著者は1冊の書籍を執筆している。
④HasForeignKey<T>:
protected override void OnModelCreating (ModelBuilder modelBuilder) { // 1対1のリレーションシップ modelBuilder.Entity(entity => { // 書籍は1人の著者が存在し、著者は1つの書籍を執筆している entity.HasOne(b => b.Author) .WithOne(a => a.Book) .HasForeignKey(a => a.BookId) .HasConstraintName("book_fkey"); // 外部キーの名称を任意で設定する });
1対多
以下のCompanyテーブルとEmployeeテーブルで、1つの会社に複数人の会社員が紐づいていると仮定します。
public class Company { public int Id { get; set; } public string Name { get; set; } } public class Employee { public int Id { get; set; } public string Name { get; set; } }
まず、各テーブルにナビゲーションプロパティと外部キーを記述します。1つの会社には複数人の会社員が存在するので、CompanyクラスのナビゲーションプロパティをICollection型にします。
public class Company { public int Id { get; set; } public string Name { get; set; } // ナビゲーションプロパティ public ICollection<Employee> Employees { get; set; }} public class Employee { public int Id { get; set; } public string Name { get; set; } // 外部キー public int CompanyId { get; set; } // ナビゲーションプロパティ public Company Company { get; set; } }
次に、DbContext.csのOnModelCreatingで、FluentAPIを使ってリレーションシップを設定します。おそらく、こちらのFluentAPIの部分で混乱してしまう方がいらっしゃると思いますが、次のように置き換えて考えてみてください。
①modelBuilder.Entity<T>:
②HasMany(c => c.Employees):Company(会社)は複数人の会社員(Employees)存在し、
③WithOne(e => e.Comany):それぞれの会社員は1つの会社に所属している。
④HasForeignKey(e => e.CompanyId):外部キーを指定する。
protected override void OnModelCreating (ModelBuilder modelBuilder) { // 1対多のリレーションシップ modelBuilder.Entity(entity => { // 会社は複数の会社員が存在し、それぞれの会社員は1つの会社に所属している entity.HasMany(c => c.Employees) .WithOne(e => e.Company) .HasForeignKey(e => e.CompanyId) .HasConstraintName("company_fkey"); // 外部キーの名称を任意で設定する }); }
多対多
以下のBookテーブルとCategoryテーブルで、1冊の書籍は複数のカテゴリーに表示され、1つのカテゴリーには多数の書籍が含まれていると仮定します。
public class Book { public int Id { get; set; } public string Title { get; set; } public int Price { get; set; } } public class Category { public int Id { get; set; } public string Name { get; set; } }
多対多のリレーションシップは直接設定することができないので、通常のテーブル設計に倣ってBookCategoryの中間テーブルを追加し、プロパティには各テーブルの主キーとナビゲーションプロパティを記述します。
また、Book・Categoryテーブルにも中間テーブルのBookCategoryに対して、ICollection型のナビゲーションプロパティを設定します。
public class Book { public int Id { get; set; } public string Title { get; set; } public int Price { get; set; } public ICollection<BookCategory> BookCategories { get; set; } } public class Category { public int Id { get; set; } public string Name { get; set; } public ICollection<BookCategory> BookCategories { get; set; } } // 中間テーブルを追加 public class BookCategory { // Bookテーブルの主キーとナビゲーションプロパティ public int BookId { get; set; } public Book Book { get; set; } // Categoryテーブルの主キーとナビゲーションプロパティ public int CategoryId { get; set; } public Category Category { get; set; } }
次に、DbContext.csのOnModelCreatingで、FluentAPIを使ってリレーションシップを設定します。おそらく、こちらのFluentAPIの部分で混乱してしまう方がいらっしゃると思いますが、次のように置き換えて考えてみてください。
①modelBuilder.Entity<T>:
②HasKey(bc => new { bc.BookId, bc.CategoryId}):中間テーブルの主キーは、両方の外部キー値で構成される複合主キーとなる。
①modelBuilder.Entity<T>:
②HasOne(bc => bc.Book):BookCategoryは1つの書籍を保持し、
③WithMany(b => b.BookCategories):1つの書籍は複数のBookCategoryを保持する。
④HasForeignKey(bc => bc.BookId):外部キーを設定する。
①modelBuilder.Entity<T>:
②HasOne(bc => bc.Category):BookCategoryは1つのカテゴリーを保持し、
③WithMany(b => b.BookCategories):1つのカテゴリーは複数のBookCategoryを保持する。
④HasForeignKey(bc => bc.CategoryId):外部キーを設定する。
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 多対多のリレーションシップ modelBuilder.Entity<BookCategory>() .HasKey(bc => new { bc.BookId, bc.CategoryId }); // HasKeyメソッドで複合主キーを設定 modelBuilder.Entity<BookCategory>() .HasOne(bc => bc.Book) .WithMany(b => b.BookCategories) .HasForeignKey(bc => bc.BookId); modelBuilder.Entity<BookCategory>() .HasOne(bc => bc.Category) .WithMany(c => c.BookCategories) .HasForeignKey(bc => bc.CategoryId); }
最後に、中間テーブルのBookCategoryをコンテキストに追加します。DBへのデータの挿入・読み取り・更新・削除といった基本的な操作はDbContextを通じて行ないます。DbContextは内部に挿入や更新を行なうオブジェクトの状態を保持しています。
public class SampleDbContext : DbContext { public DbSet Books { get; set; } public DbSet Categories { get; set; } public DbSet BookCategories { get; set; } }
今回はEntity Framework Coreでリレーションシップを設定する方法について紹介させていただきました。少しでもお役に立てれば幸いです。
参考記事リレーションシップ - EF Core | Microsoft DocsDocumentation and Tutorials | Learn Entity Framework Core
あおい/FIXER
「初心者の方にも分かりやすく」をモットーにブログ執筆。TWICEミナペン。普段はC#とSQL Server、たまにPower Platformを触ってます。書籍『Microsoft Power Platformローコード開発[活用]入門 - 現場で使える業務アプリのレシピ集』