C#のEF Coreでデータベースのリレーション(親子関係)を定義する方法を徹底解説!
生徒
「C#のEntity Framework Coreを使って、複数のテーブルを繋げる方法を知りたいです。リレーションって何ですか?」
先生
「リレーションとは、複数のデータ同士の繋がりのことです。例えば、一つの会社に複数の社員がいるような関係を指します。」
生徒
「難しそうですね。プログラミングでそれをどうやって表現するんですか?」
先生
「Entity Framework Core(EF Core)を使えば、クラスを作るだけでその繋がりを簡単に定義できます。まずは基本から一緒に見ていきましょう!」
1. データベースのリレーションとは?
データベースという言葉を初めて聞く方もいるかもしれません。データベースとは、コンピュータの中でたくさんの情報を整理して保存しておく箱のようなものです。C#で本格的なアプリを作る際、このデータベースは欠かせない存在です。
そして、今回のテーマであるリレーションとは、日本語で「関係」を意味します。データベースの中には複数の表(テーブル)がありますが、それらをバラバラに置いておくのではなく、お互いに関連性を持たせることが重要です。これをリレーションシップと呼びます。
例えば、「注文」というデータがあったとき、その注文が「どの顧客」によるものなのかを知る必要がありますよね。このように、一つのデータが別のデータと紐付いている状態をリレーションと言います。EF Coreという便利なツールを使うと、この複雑な仕組みをC#のコードで簡単に扱うことができるようになります。
2. Entity Framework Core(EF Core)の役割
プログラミング未経験の方にとって、データベースを直接操作するのは非常に大変な作業です。通常はSQLという専用の言語を学ばなければなりませんが、C#にはEntity Framework Core(EF Core)という強力な味方がいます。
EF Coreは「ORM(オブジェクト関係マッピング)」と呼ばれる種類のものですが、簡単に言うと「C#のクラス(設計図)を、自動的にデータベースのテーブル(表)に変換してくれる翻訳機」のようなものです。これを使うことで、私たちはSQLを細かく書かなくても、いつものC#の書き方でデータベースにデータを保存したり、リレーションを定義したりすることができるのです。
特に「外部キー」という、テーブル同士を繋ぐための目印も、EF Coreが賢く判断して作成してくれます。まずは、この便利なツールを使って、データ同士を連結させる感覚を掴んでいきましょう。
3. 外部キーと親子関係の基本
リレーションを理解する上で最も大切な言葉が外部キー(Foreign Key)です。これは、別のテーブルのデータを指し示すための識別番号のようなものです。
リレーションには主に「親」と「子」の関係があります。これを一対多(いちたいた)の関係と呼びます。例えば、「一人の著者(親)」が「たくさんの本(子)」を書くという関係です。このとき、本のデータの中に「この本はどの著者が書いたか」を示すための著者IDを持たせます。この「著者ID」こそが外部キーです。
EF Coreでは、クラスの中に相手のクラスをプロパティとして持たせることで、この親子関係を定義します。以下のコードは、最もシンプルな親(部署)と子(社員)の関係を定義する例です。
// 親となるクラス(部署)
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
// 子のリストを持つ(一対多の「多」側を保持)
public List<Employee> Employees { get; set; }
}
// 子となるクラス(社員)
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
// 外部キー:どの部署に所属しているか
public int DepartmentId { get; set; }
// ナビゲーションプロパティ:親への参照
public Department Department { get; set; }
}
4. ナビゲーションプロパティという魔法
先ほどのコードの中にpublic Department Department { get; set; }という記述がありました。これはC#の世界でナビゲーションプロパティと呼ばれます。これは単なるデータではなく、データ同士を結ぶ「架け橋」のような役割を果たします。
このプロパティがあるおかげで、社員のデータから「その社員が所属している部署の名前」をすぐに手に入れることができます。もしこの仕組みがなければ、社員のデータから部署IDを調べ、さらに部署のテーブルからそのIDを探すという二度手間が必要になります。
EF Coreはこのナビゲーションプロパティを見つけると、「あ、この二つのクラスは親子なんだな」と自動的に判断して、データベースの中に適切な外部キーを設定してくれます。プログラミング初心者の方は、まず「相手を指し示す名前のプロパティを準備する」と覚えておけば大丈夫です。
5. DBContextでテーブルの繋がりを登録する
クラスを作っただけでは、まだデータベースには反映されません。次に必要なのがDbContextというクラスです。これはデータベース全体の管理者のような役割で、どのクラスをテーブルとして扱うかを宣言します。
ここで、先ほど作った「Department」と「Employee」を登録することで、EF Coreは初めて二つの関係性をデータベース上に構築する準備を整えます。以下のコードのように記述します。
using Microsoft.EntityFrameworkCore;
public class MyAppContext : DbContext
{
// データベース上のテーブルとして登録
public DbSet<Department> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
// 今回はメモリ上のデータベースを使用する設定(テスト用)
options.UseInMemoryDatabase("MySampleDb");
}
}
このように、DbSetを使って各クラスを登録します。これにより、EF Coreはクラス間のリレーションを読み取り、裏側で「EmployeeテーブルのDepartmentIdは、DepartmentテーブルのIdを参照する外部キーである」という設定を行ってくれるのです。
6. 実際にデータを追加してリレーションを確認する
それでは、定義したリレーションを使って、実際にデータを保存してみましょう。親である「部署」を一つ作り、そこに複数の「社員」を紐付けて保存する流れを確認します。
注目してほしいのは、社員を追加する際に、わざわざ番号(ID)を手動で入力しなくても、親のオブジェクトをそのまま渡すだけで繋がることができる点です。これがEF Coreの非常に強力で便利なところです。
using (var context = new MyAppContext())
{
// 1. 親となる部署を作成
var salesDept = new Department { Name = "営業部" };
// 2. 子となる社員を作成し、部署に紐付ける
var emp1 = new Employee { Name = "田中さん", Department = salesDept };
var emp2 = new Employee { Name = "佐藤さん", Department = salesDept };
// 3. データをデータベースに保存する準備
context.Departments.Add(salesDept);
context.Employees.AddRange(emp1, emp2);
// 4. 実際に保存を実行
context.SaveChanges();
Console.WriteLine("データの保存が完了しました!");
}
このコードを実行すると、データベース内では自動的に田中さんと佐藤さんに「営業部」と同じIDが割り振られます。これが外部キーによるリレーションの構築です。
7. データを取得する際の注意点(Eager Loading)
リレーションを定義した後、データを取り出すときには一つ注意点があります。それは「デフォルトでは親のデータしか持ってこない」という点です。例えば、部署のデータを取り出しただけでは、その部署に所属する社員のリストは空っぽのままです。
関連するデータも一緒に持ってきたいときは、Includeという命令を使います。これをEager Loading(イガーローディング:積極的な読み込み)と呼びます。まるで、メインの荷物と一緒に付属品もまとめて取り寄せてもらうようなイメージです。
using (var context = new MyAppContext())
{
// Includeを使って、部署と一緒に所属社員も取得する
var dept = context.Departments
.Include(d => d.Employees)
.FirstOrDefault(d => d.Name == "営業部");
if (dept != null)
{
Console.WriteLine($"部署名: {dept.Name}");
foreach (var emp in dept.Employees)
{
Console.WriteLine($"--- 社員名: {emp.Name}");
}
}
}
実行結果は以下のようになります。
部署名: 営業部
--- 社員名: 田中さん
--- 社員名: 佐藤さん
もしIncludeを使わなければ、社員を表示する部分で何も出てこなかったり、エラーになったりします。リレーションを使う際は、この「一緒に読み込む」という操作をセットで覚えておきましょう。
8. 一対一や多対多のリレーション
これまでは「一対多」の関係を見てきましたが、世の中には他にもリレーションの形があります。例えば、「一人のユーザー」に「一つのプロフィール」という一対一(いちたいいち)の関係。あるいは、「一人の学生」が「複数の授業」を受け、「一つの授業」に「複数の学生」がいる多対多(たたいた)の関係です。
一対一の場合は、両方のクラスにお互いのプロパティを一つずつ持たせます。多対多の場合は、お互いにリスト形式のプロパティを持たせます。EF Coreは非常に優秀なので、これらの書き方を変えるだけで、自動的に適切なデータベースの構造を考えてくれます。
初心者の方は、まず基本の「一対多」を完璧にマスターすることを目指しましょう。会社の部署と社員、ブログの記事とコメント、ショッピングサイトの注文と注文商品など、身の回りのほとんどのデータはこの「一対多」で説明がつきます。
9. 名前付けのルール(規約)を覚えよう
EF Coreには「このように名前をつければ、自動でリレーションだと解釈する」というルール(規約)があります。これを守るだけで、難しい設定を書かなくてもリレーションが完成します。
最も一般的なルールは、外部キーの名前を「相手のクラス名 + Id」にすることです。例えば、Departmentクラスに関連付けるなら、プロパティ名をDepartmentIdにします。これだけで、EF Coreは「これはDepartmentテーブルへの外部キーだ!」と認識してくれます。
このように、システムが自動的に意図を汲み取ってくれる仕組みを規則(Convention)と呼びます。プログラミングでは「設定より規約」という言葉があるほど、このルールに従うことが推奨されています。ルールを守ることで、コードがスッキリし、他の人が見たときにも理解しやすいプログラムになります。
10. エラーが出たときのチェックポイント
リレーションを定義しているときに、うまく動かないこともあるでしょう。そんなときは、以下のポイントを順番に確認してみてください。
一つ目は、IDの型が一致しているかです。親のIdがint(数値)なのに、子の外部キーをstring(文字)にしているとエラーになります。二つ目は、DbContextに両方のクラスを登録しているかです。片方だけでは関係性が成立しません。
三つ目は、マイグレーションという作業を忘れていないかです。プログラムを書き換えた後は、その変更をデータベースの設計図に反映させる「マイグレーション」というコマンドを実行する必要があります。パソコンに「新しい関係性ができたから、本番の箱の中身も作り替えてね」と命令するイメージです。これらの基本を抑えておけば、リレーションの構築で迷うことは少なくなるはずです。