KENTEM TechBlog

建設業のDXを実現するKENTEMの技術ブログです。

【C#】.NET10 EF Coreで「LEFT JOIN」が正式サポート!

この記事は、 KENTEM TechBlog アドベントカレンダー2025 2日目、12月2日の記事です。

先日、ついに .NET 10 が正式リリースされました!
今回は3年間の長期サポート(LTS)バージョンということで、アップデートを検討されている方も多いのではないでしょうか?

.NET 10では、C#のバージョンが 14 になり、より便利な機能が追加されています。
↓ 一例

また、Entity Framework Core(EF Core) にも多くの新機能が加わっています。
そんな中でも特に注目したいのが、SQLでおなじみの 「LEFT JOIN」 が正式にサポートされたことです。
今回は、この新機能の概要についてご紹介します!

前置き

サンプル構成


シンプルな2つのテーブルに対して結合処理をしていくサンプルを用意しました。

今までの書き方(~.NET9)

INNER JOIN(内部結合)

本題に入る前にシンプルな「INNER JOIN」から説明します。
こちらは以前から普通に使えていました。

context.Users
       .Join(context.Items, 
             user => user.Id, 
             item => item.UserId,
            (user, item) => new 
            {
                user.Name,
                item.ItemName 
            });
太郎 | ひのきのぼう
花子 | こんぼう
二郎 | てつのぼう
太郎 | きんののべぼう

普通に分かりやすいLINQで書けます!

LEFT (OUTER) JOIN(左外部結合)

「LEFT JOIN」も使うことはよくあると思いますが、一方こちらは書き方が非常に複雑でした・・・。

context.Users
       .GroupJoin(context.Items,
                  user => user.Id,
                  item => item.UserId,
                 (user, items) => new { user, items })
       .SelectMany(
           ui => ui.items.DefaultIfEmpty(),
          (ui, item) => new 
          {
               ui.user.Name,
               ItemName = item.ItemName ?? "(なし)" 
          });
太郎  | きんののべぼう
太郎  | ひのきのぼう
花子  | こんぼう
二郎  | てつのぼう
ボブ美 | (なし)

GroupJoinSelectManyDefaultIfEmptyを駆使してやっと実現できます。
正直かなり辛い感じだと思いますし、ミスもしやすい気がします。

.NET 10以降の書き方

LEFT JOIN

やっと本題です!以下が.NET10対応版のコードです!

context.Users
       .LeftJoin(context.Items,
                 user => user.Id,
                 item => item.UserId,
                (user, item) => new 
                {
                    user.Name, 
                    ItemName = item.ItemName ?? "(なし)" 
                });
太郎  | きんののべぼう
太郎  | ひのきのぼう
花子  | こんぼう
二郎  | てつのぼう
ボブ美 | (なし)

ほぼ、というか「INNER JOIN」と書き方が全く一緒ですね!
これでミスは極端に減ると思います!(ヤッタネ)

発行されるSQLに違いはある?

.NET9

context.Users
       .GroupJoin(context.Items,
                  user => user.Id,
                  item => item.UserId,
                 (user, items) => new { user, items })
       .SelectMany(
           ui => ui.items.DefaultIfEmpty(),
          (ui, item) => new 
          {
               ui.user.Name,
               ItemName = item.ItemName ?? "(なし)" 
          });
SELECT "u"."Name", COALESCE("i"."ItemName", '(なし)') AS "ItemName"
      FROM "Users" AS "u"
      LEFT JOIN "Items" AS "i" ON "u"."Id" = "i"."UserId"

.NET10

context.Users
       .LeftJoin(context.Items,
                 user => user.Id,
                 item => item.UserId,
                (user, item) => new 
                {
                     user.Name, 
                     ItemName = item.ItemName ?? "(なし)" 
                });
SELECT "u"."Name", COALESCE("i"."ItemName", '(なし)') AS "ItemName"
      FROM "Users" AS "u"
      LEFT JOIN "Items" AS "i" ON "u"."Id" = "i"."UserId"

かなり形は違っているものの、発行されるSQLは全く一緒でした。
安心してお使いください!

補足

RIGHT (OUTER) JOIN(右外部結合)

.NET10では「RIGHT JOIN」も追加されています。

context.Users
       .RightJoin(context.Items,
                  user => user.Id,
                  item => item.UserId,
                 (user, item) => new 
                 {
                      UserName = user.Name ?? "(なし)",
                      item.ItemName 
                 });

ただ、個人的に「RIGHT JOIN」を使うケースが思いつかないので、特には語りません・・・。

クエリ構文

今までの例は「メソッド構文」で語ってきました。
「クエリ構文」愛好家の方には残念ですが、「LEFT JOIN」の対応は現時点ではサポートされないようです。
書くとしたらやはり下記のようになってしまいます・・・。
対応を待ちましょう!

from user in context.Users
join item in context.Items
on user.Id equals item.UserId into itemGroup
from item in itemGroup.DefaultIfEmpty()
select new 
{
    user.Name,
    ItemName = item.ItemName ?? "(なし)"
};

おわりに

KENTEMでは、様々な拠点でエンジニアを大募集しています! 建設×ITにご興味頂いた方は、是非下記のリンクからご応募ください。 recruit.kentem.jp career.kentem.jp