Entity Framework Core でテーブルのJOIN (テーブル結合)をする - C#

Entity Framework Core でテーブルのJOIN (テーブル結合)をするコードを紹介します。

概要

Entity Framework Core でテーブルをJOINして対応する値を取得するコードを紹介します。
ヒント
LINQの結果を受け取る際に匿名型を利用しないコードはこちらの記事を参照してください。

書式

クエリ構文

JOINしすべてのフィールドを取得する場合の書式は以下になります。
結果は匿名型(Anonymous型)で戻るため、IQueryable<dynamic> 型の変数に代入します。
from (結合元テーブルの一時名) in (結合元テーブルのDbSet) join (結合先テーブルの一時名) in (結合先テーブルのDbSet)
on (結合元テーブルの一時名).(JOIN対象のフィールド) equals (結合先テーブルの一時名).(JOIN対象のフィールド) select new {(結合元テーブルの一時名), (結合先テーブルの一時名)};

メソッド構文

JOINしすべてのフィールドを取得する場合の書式は以下になります。 結果は匿名型(Anonymous型)で戻るため、IQueryable<dynamic> 型の変数に代入します。
(結合元テーブルのDbSet).Join(
(結合先テーブルのDbSet), (結合元テーブルの一時名) => (結合元テーブルの一時名).(JOIN対象のフィールド),
(結合先テーブルの一時名)=> (結合先テーブルの一時名).(JOIN対象のフィールド),
( (結合元テーブルの返却オブジェクトのフィールド名), (結合先テーブルの返却オブジェクトのフィールド名) ) => new { (結合元テーブルの返却オブジェクトのフィールド名), (結合先テーブルの返却オブジェクトのフィールド名) });

記述例

クエリ構文

IQueryable<dynamic> rec = from pd_table in sc.ProductsDs join pdsp_table in sc.ProductsDstockPlaces
  on pd_table.Stockplace equals pdsp_table.Id select new {pd_table, pdsp_table };

メソッド構文

IQueryable<dynamic> rec = sc.ProductsDs.Join(
  sc.ProductsDstockPlaces,
  pd_table => pd_table.Stockplace,
  pdsp_table => pdsp_table.Id,
  (pd_table, pdsp_table) => new { pd_table, pdsp_table });

プログラム例

テーブルの準備

データベースに以下のテーブルを用意します。
ProductsD テーブルのstockplace フィールドと ProductsDStockPlace テーブルの id フィールドを結合した結果を取得します。
ProductsD テーブル
id name stockplace category price
1 ぺんぎんクッキー 1 1 180
2 らくだキャラメル 3 2 95
3 しろくまアイス 2 3 230
4 かるがもサブレ 1 1 160
5 あるぱかカステラ 2 2 420
ProductsDStockPlace テーブル
id name
1 東京
2 札幌
3 福岡

Entity Framework Core のインストールとScaffold-DbContext

Entity Framework Coreのパッケージをインストールし、Scaffold-DbContextを実行します。
手順の詳細はこちらの記事を参照してください。

UI

下図のフォームを作成します。複数行のテキストボックスとボタンを2つ配置します。

コード

以下のコードを記述します。[button1]と[button2]のClickイベントハンドラを実装します。
using EntityFrameworkCoreJoin.Models;

namespace EntityFrameworkCoreJoin
{
  public partial class FormMain : Form
  {
    public FormMain()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      IQueryable<dynamic> rec;
      IPentecSandBoxContext sc = new IPentecSandBoxContext();

      rec = sc.ProductsDs.Join(sc.ProductsDstockPlaces, pd_table => pd_table.Stockplace, pdsp_table => pdsp_table.Id, (pd_table, pdsp_table) => new { pd_table, pdsp_table });

      foreach (var r in rec) {
        textBox1.Text += string.Format("{0} \\{1:d} - {2}\r\n", r.pd_table.Name.Trim(), r.pd_table.Price, r.pdsp_table.Name.Trim());
      }
    }

    private void button2_Click(object sender, EventArgs e)
    {
      IQueryable<dynamic> rec;

      IPentecSandBoxContext sc = new IPentecSandBoxContext();

      rec = from pd_table in sc.ProductsDs join pdsp_table in sc.ProductsDstockPlaces on pd_table.Stockplace equals pdsp_table.Id select new { pd_table, pdsp_table };

      foreach (var r in rec) {
        textBox1.Text += string.Format("{0} \\{1:d} - {2}\r\n", r.pd_table.Name.Trim(), r.pd_table.Price, r.pdsp_table.Name.Trim());
      }
    }

    private void FormMain_Load(object sender, EventArgs e)
    {

    }
  }
}

解説

LINQを利用して、2つのテーブルのJOINを取得します。[button1]がメソッド構文、[button2]がクエリ構文での記述です。
rec = sc.ProductsDs.Join(sc.ProductsDstockPlaces, pd_table => pd_table.Stockplace, pdsp_table => pdsp_table.Id, (pd_table, pdsp_table) => new { pd_table, pdsp_table });
rec = from pd_table in sc.ProductsDs join pdsp_table in sc.ProductsDstockPlaces on pd_table.Stockplace equals pdsp_table.Id select new { pd_table, pdsp_table };

取得した結果をテキストボックスに表示します。
  foreach (var r in rec) {
    textBox1.Text += string.Format("{0} \\{1:d} - {2}\r\n", r.pd_table.Name.Trim(), r.pd_table.Price, r.pdsp_table.Name.Trim());
  }

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[button1]をクリックします。下図の結果がテキストボックスに表示されます。 ProductsD テーブルのstockplace フィールドと ProductsDStockPlace テーブルの id フィールドが結合でき、 stockplace フィールドに対応する、ProductsDStockPlace テーブルのNameの値を出力できています。


[button2]をクリックします。下図の結果がテキストボックスに表示されます。同じ結果が得られます。

補足

LINQのJOINメソッドの第四引数はテーブルの一時名と合わせなくても良いです。以下のコードでも動作します。
  private void button1_Click(object sender, EventArgs e)
  {
    IQueryable<dynamic> rec;
    IPentecSandBoxContext sc = new IPentecSandBoxContext();

    rec = sc.ProductsDs.Join(sc.ProductsDstockPlaces, pd_table => pd_table.Stockplace, pdsp_table => pdsp_table.Id, (a, b) => new { a, b });
    foreach (var r in rec) {
      textBox1.Text += string.Format("{0} \\{1:d} - {2}\r\n", r.a.Name.Trim(), r.a.Price, r.b.Name.Trim());
    }
  }

プログラム例2: 3つのテーブルをJOIN

テーブル

3つのテーブルをJOINします。
ProductsD テーブル
id name stockplace category price
1 ぺんぎんクッキー 1 1 180
2 らくだキャラメル 3 2 95
3 しろくまアイス 2 3 230
4 かるがもサブレ 1 1 160
5 あるぱかカステラ 2 2 420
ProductsDStockPlace テーブル
id name
1 東京
2 札幌
3 福岡
ProductsDCategory テーブル
id name
1 焼き菓子
2 生菓子
3 氷菓

UI

下図のフォームを作成します。ボタンは[button4] [button5]を利用します。

コード

下記コードを記述します。
using EntityFrameworkCoreJoin.Models;

namespace EntityFrameworkCoreJoin
{
  public partial class FormMain : Form
  {
    public FormMain()
    {
      InitializeComponent();
    }

    private void FormMain_Load(object sender, EventArgs e)
    {

    }

    private void button4_Click(object sender, EventArgs e)
    {
      IQueryable<dynamic> rec;
      IPentecSandBoxContext sc = new IPentecSandBoxContext();

      rec = sc.ProductsDs
        .Join(sc.ProductsDstockPlaces, pd_table => pd_table.Stockplace, pdsp_table => pdsp_table.Id, (pd_table, pdsp_table) => new { pd_table, pdsp_table })
        .Join(sc.ProductsDcategories, pd2_table => pd2_table.pd_table.Category, pdcat_table => pdcat_table.Id, (pd2_table, pdcat_table) => new { pd2_table.pd_table, pd2_table.pdsp_table, pdcat_table });

      foreach (var r in rec) {
        textBox1.Text += string.Format("{0} \\{1:d} - {2} - {3}\r\n",
          r.pd_table.Name.Trim(),
          r.pd_table.Price,
          r.pdsp_table.Name.Trim(),
          r.pdcat_table.Name.Trim()
          );
      }

    }

    private void button5_Click(object sender, EventArgs e)
    {
      IQueryable<dynamic> rec;
      IPentecSandBoxContext sc = new IPentecSandBoxContext();

      rec = from pd_table in sc.ProductsDs
            join pdsp_table in sc.ProductsDstockPlaces on pd_table.Stockplace equals pdsp_table.Id
            join pdcat_table in sc.ProductsDcategories on pd_table.Category equals pdcat_table.Id
            select new { pd_table, pdsp_table, pdcat_table };

      foreach (var r in rec) {
        textBox1.Text += string.Format("{0} \\{1:d} - {2} - {3}\r\n", 
          r.pd_table.Name.Trim(), r.pd_table.Price, r.pdsp_table.Name.Trim(), r.pdcat_table.Name.Trim());
      }
    }
  }
}

解説

LINQを利用して、3つのテーブルのJOINを取得します。[button4]がメソッド構文、[button5]がクエリ構文での記述です。
メソッド構文の場合は、2つのJOINの結果に対して、メソッドチェーンの記述でJOINメソッドを呼び出します。
2番目のJoinメソッドでは、第2引数のパラメーターが、1番目のJoinメソッドの結果となるため、匿名型の構造型 { pd_table, pdsp_table} が入力パラメーターとなる点に注意が必要です。
2番目のJoinの第四パラメーターでは、new {pd2_table, pdcat_table}の記述も可能ですが、結果が2段のネストにならないコード記述をしています。
  rec = sc.ProductsDs
    .Join(sc.ProductsDstockPlaces, pd_table => pd_table.Stockplace, pdsp_table => pdsp_table.Id, (pd_table, pdsp_table) => new { pd_table, pdsp_table })
    .Join(sc.ProductsDcategories, pd2_table => pd2_table.pd_table.Category, pdcat_table => pdcat_table.Id, (pd2_table, pdcat_table) => new { pd2_table.pd_table, pd2_table.pdsp_table, pdcat_table });

クエリ構文の場合は、join句を2つ続けて並べ、select句で戻り値の構造を記述します。
  rec = from pd_table in sc.ProductsDs
        join pdsp_table in sc.ProductsDstockPlaces on pd_table.Stockplace equals pdsp_table.Id
        join pdcat_table in sc.ProductsDcategories on pd_table.Category equals pdcat_table.Id
        select new { pd_table, pdsp_table, pdcat_table };

LINQで取得したレコードの値を以下のコードでテキストボックスに表示しています。
  foreach (var r in rec) {
    textBox1.Text += string.Format("{0} \\{1:d} - {2} - {3}\r\n", 
      r.pd_table.Name.Trim(), r.pd_table.Price, r.pdsp_table.Name.Trim(), r.pdcat_table.Name.Trim());
  }

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[button4]をクリックします。下図の表示結果となります。 ProductsD テーブルのstockplace フィールドと ProductsDStockPlace テーブルの id フィールドが結合され、 ProductsD テーブルのcategory フィールドと ProductsDCategory テーブルの id フィールドが結合でき、 stockplace フィールドに対応する、ProductsDStockPlace テーブルのNameの値と、category フィールドとに対応する、ProductsDCategory テーブルのNameの値が出力できています。


[button5]をクリックした場合も[button4]と同じ結果が表示されます。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2023-07-08
作成日: 2023-06-17
iPentec all rights reserverd.