Entity Framework Core でGROUP JOINの結果を1次元のデータ構造に展開する - SelectManyの利用 - C#

Entity Framework Core でGROUP JOINの結果を1次元のデータ構造に展開するコードを紹介します。

概要

こちらの記事では、GroupJoinメソッドを利用して、テーブルを結合し結果を取得しました。 結果は構造化され、Join元のレコードに対するJoin先のレコードが配列構造で取得できました。 構造化された状態は情報の管理や扱いが分かりやすいですが、すべての要素にアクセスする場合は、 ネストされたループ処理を記述する必要があり、ロジックがグく雑になります。 そのため、利用用途によっては、構造化された状態ではなく、1次元のデータ構造で結果を取得したい場合があります。
この記事では、SelectMany メソッドを利用して、GroupJoinの結果を1次元のデータ構造に変えるコードを紹介します。

書式

.SelectMany([入力オブジェクトの一時名] => [出力データのロジック])

プログラム例:1

GroupJoinの結果からJOIN先のレコードのLabelの値をstring型のIQueryable型(1次元リスト)で返すプログラムです。

UI

下図のフォームを作成します。

コード

以下のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static EntityFrameworkCoreJoin.FormGroupJoin;
using EntityFrameworkCoreJoin.Models;
using static EntityFrameworkCoreJoin.FormSelectMany;

namespace EntityFrameworkCoreJoin
{
  public partial class FormSelectMany : Form
  {
    public class QueryResult
    {
      public HstLevel1 hst1;
      public IEnumerable<HstLevel2> hst2;
    }

    public FormSelectMany()
    {
      InitializeComponent();
    }

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

      //メソッド形式
      rec = sc.HstLevel1s.GroupJoin(sc.HstLevel2s, h1 => h1.Id, h2 => h2.Parent, (h1, h2) => new QueryResult() { hst1 = h1, hst2 = h2 })
        .SelectMany(rr => rr.hst2.Select(c => c.Label));

      foreach (string r in rec) {
        textBox1.Text += string.Format("{0}\r\n", r.Trim());
      }
    }
  }
}

クエリ形式を利用する場合は以下のコードを利用します。
    private void button1_Click(object sender, EventArgs e)
    {
      IQueryable<string> rec;
      IPentecSandBoxContext sc = new IPentecSandBoxContext();

      //クエリ形式
      rec = from h1 in sc.HstLevel1s join h2 in sc.HstLevel2s on h1.Id equals h2.Parent into groupjoin from row in groupjoin select row.Label;

      foreach (string r in rec) {
        textBox1.Text += string.Format("{0}\r\n", r.Trim());
      }
    }

解説

GroupJoinの結果からJOIN先のレコードのLabelの値をstring型のIQueryable型(1次元リスト)で返す処理です。
GroupJoinについてはこちらの記事を参照してください。
  //メソッド形式
  rec = sc.HstLevel1s.GroupJoin(sc.HstLevel2s, h1 => h1.Id, h2 => h2.Parent, (h1, h2) => new QueryResult() { hst1 = h1, hst2 = h2 })
    .SelectMany(rr => rr.hst2.Select(c => c.Label));

SelectManyメソッドには、QueryResultオブジェクトをパラメータにとり、戻り値を返すデリゲートを与えます。今回のコードでは、 hst2メンバのLabelの値を返します。
  .SelectMany(rr => rr.hst2.Select(c => c.Label));

  //クエリ形式
  rec = from h1 in sc.HstLevel1s join h2 in sc.HstLevel2s on h1.Id equals h2.Parent into groupjoin from row in groupjoin select row.Label;

実行結果

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


[button1]をクリックします。下図の結果が表示されます。GROUP JOIN の結果のJOIN先テーブルのレコードのLabelの値が表示されます。

プログラム例:2

GroupJoinの結果からJOIN先のレコードのクラスの値をQueryResultMany型のIQueryable型(1次元リスト)で返すプログラムです。

UI

下図のフォームを作成します。

コード

以下のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static EntityFrameworkCoreJoin.FormGroupJoin;
using EntityFrameworkCoreJoin.Models;
using static EntityFrameworkCoreJoin.FormSelectMany;

namespace EntityFrameworkCoreJoin
{
  public partial class FormSelectMany : Form
  {
    public class QueryResult
    {
      public HstLevel1 hst1;
      public IEnumerable<HstLevel2> hst2;
    }
    public class QueryResultMany
    {
      public string LabelP;
      public string ValueP;
      public string Label;
      public string Value;
    }

    public FormSelectMany()
    {
      InitializeComponent();
    }

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

      //メソッド形式
      rec = sc.HstLevel1s.GroupJoin(sc.HstLevel2s, h1 => h1.Id, h2 => h2.Parent, (h1, h2) => new QueryResult() { hst1 = h1, hst2 = h2 })
         .SelectMany(rr => rr.hst2.Select(c => new QueryResultMany() {LabelP=rr.hst1.Label, ValueP=rr.hst1.Value, Label = c.Label, Value=c.Value }));

      foreach (QueryResultMany r in rec) {
        textBox1.Text += string.Format("{0}:{1}:{2}:{3}\r\n", r.LabelP.Trim(),r.ValueP.Trim(),r.Label.Trim(),r.Value.Trim());
      }
    }
  }
}

クエリ式を利用する場合は以下のコードになります。
    private void button2_Click(object sender, EventArgs e)
    {
      IQueryable<QueryResultMany> rec;
      IPentecSandBoxContext sc = new IPentecSandBoxContext();
      
      //クエリ形式
      rec = from h1 in sc.HstLevel1s join h2 in sc.HstLevel2s on h1.Id equals h2.Parent into groupjoin select new QueryResult(){ hst1=h1, hst2=groupjoin };

      foreach (QueryResultMany r in rec) {
        textBox1.Text += string.Format("{0}:{1}:{2}:{3}\r\n", r.LabelP.Trim(),r.ValueP.Trim(),r.Label.Trim(),r.Value.Trim());
      }

解説

GroupJoinの結果からJOIN先のレコードのLabelの値をstring型のIQueryable型(1次元リスト)で返す処理です。
GroupJoinについてはこちらの記事を参照してください。
  //メソッド形式
  rec = sc.HstLevel1s.GroupJoin(sc.HstLevel2s, h1 => h1.Id, h2 => h2.Parent, (h1, h2) => new QueryResult() { hst1 = h1, hst2 = h2 })
     .SelectMany(rr => rr.hst2.Select(c => new QueryResultMany() {LabelP=rr.hst1.Label, ValueP=rr.hst1.Value, Label = c.Label, Value=c.Value }));

SelectManyメソッドには、QueryResultオブジェクトをパラメータにとり、戻り値を返すデリゲートを与えます。今回のコードでは、 QueryResultManyオブジェクトを返し、レコードのクラスのメンバの値を代入して返します。
.SelectMany(rr => rr.hst2.Select(c => new QueryResultMany() {LabelP=rr.hst1.Label, ValueP=rr.hst1.Value, Label = c.Label, Value=c.Value }));

実行結果

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


[button2]をクリックします。下図の結果が表示されます。GROUP JOIN の結果の値が表示されます。

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