bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] EF Core で関連データを読み込む

[C#] EF Core で関連データを読み込む

Jul 31, 2026 C# , .NET , Entity Framework Core bucket-sort

前回は、EF Core のクエリでデータを取得する方法を見ました。

今回は、関連データの読み込みを扱います。
リレーショナルデータベースでは、テーブル同士が外部キーで関連します。EF Core では、それをナビゲーションプロパティとして扱えます。

ただし、関連データをいつ読み込むかは重要です。
必要なデータを読み込まないと画面表示に困りますし、読み込みすぎると性能問題になります。

サンプルモデル

車と注文の関係を使います。

public sealed class Car
{
    public int Id { get; set; }
    public string Make { get; set; } = "";
    public string Color { get; set; } = "";
    public string PetName { get; set; } = "";

    public List<Order> Orders { get; set; } = new();
}
public sealed class Order
{
    public int Id { get; set; }
    public int CarId { get; set; }
    public string CustomerName { get; set; } = "";
    public DateTime OrderDate { get; set; }

    public Car? Car { get; set; }
}

設定です。

modelBuilder.Entity<Car>()
    .HasMany(car => car.Orders)
    .WithOne(order => order.Car)
    .HasForeignKey(order => order.CarId);

Car から複数の Order へ進めます。
Order から 1 台の Car へ戻れます。

関連データは自動では常に読み込まれない

次のように Cars だけを取得します。

Car? car = await context.Cars
    .FirstOrDefaultAsync(car => car.Id == 1);

この時点で、Orders が必ず読み込まれているとは限りません。
EF Core では、関連データの読み込み方法を選びます。

主な方法は 3 つです。

  • 最初から一緒に読み込む
  • 必要になった時点で明示的に読み込む
  • プロパティアクセス時に自動で読み込む

それぞれ見ていきます。

Include で一緒に読み込む

最初から関連データが必要だと分かっている場合は、Include() を使います。

Car? car = await context.Cars
    .Include(car => car.Orders)
    .FirstOrDefaultAsync(car => car.Id == 1);

これで、車とその注文が一緒に読み込まれます。

一覧でも使えます。

List<Car> cars = await context.Cars
    .AsNoTracking()
    .Include(car => car.Orders)
    .OrderBy(car => car.Id)
    .ToListAsync();

Include() は便利ですが、関連データが多いと結果が大きくなります。
本当に必要な関連だけを読み込みましょう。

ThenInclude でさらに深い関連を読み込む

関連のさらに先を読み込む場合は、ThenInclude() を使います。

List<Order> orders = await context.Orders
    .Include(order => order.Car)
    .ThenInclude(car => car.Features)
    .ToListAsync();

深い関連を読み込むほど、生成される SQL や結果セットが複雑になります。
表示に必要な形が決まっているなら、DTO へ投影する方が効率的な場合もあります。

投影で関連データを必要な形にする

画面表示では、エンティティ全体ではなく必要な情報だけあれば十分なことが多いです。

var cars = await context.Cars
    .AsNoTracking()
    .Select(car => new
    {
        car.Id,
        car.PetName,
        OrderCount = car.Orders.Count
    })
    .ToListAsync();

このように投影すると、必要な列と集計だけを取得できます。
Include() より SQL が軽くなることがあります。

明示的に読み込む

最初は親だけ取得し、必要になったら関連を読み込む方法です。

Car? car = await context.Cars
    .FirstOrDefaultAsync(car => car.Id == 1);

if (car != null)
{
    await context.Entry(car)
        .Collection(car => car.Orders)
        .LoadAsync();
}

参照ナビゲーションの場合は Reference() を使います。

Order? order = await context.Orders
    .FirstOrDefaultAsync(order => order.Id == 1);

if (order != null)
{
    await context.Entry(order)
        .Reference(order => order.Car)
        .LoadAsync();
}

明示的な読み込みは、「条件によって関連が必要かどうか変わる」場面で便利です。

関連データに条件を付けて読み込む

明示的な読み込みでは、関連データに対してクエリを組むこともできます。

Car? car = await context.Cars
    .FirstOrDefaultAsync(car => car.Id == 1);

if (car != null)
{
    List<Order> recentOrders = await context.Entry(car)
        .Collection(car => car.Orders)
        .Query()
        .Where(order => order.OrderDate >= DateTime.UtcNow.AddDays(-30))
        .ToListAsync();
}

全件読み込まず、必要な関連だけを取得できます。

遅延読み込み

遅延読み込みは、ナビゲーションプロパティへアクセスしたタイミングで関連データを読み込む仕組みです。

利用には追加設定が必要です。

dotnet add package Microsoft.EntityFrameworkCore.Proxies

設定例です。

optionsBuilder
    .UseLazyLoadingProxies()
    .UseSqlServer(connectionString);

ナビゲーションプロパティは virtual にします。

public sealed class Car
{
    public int Id { get; set; }
    public string Make { get; set; } = "";
    public string Color { get; set; } = "";
    public string PetName { get; set; } = "";

    public virtual List<Order> Orders { get; set; } = new();
}

これで、Orders にアクセスしたときに読み込まれる可能性があります。

Car car = await context.Cars.FirstAsync();

foreach (Order order in car.Orders)
{
    Console.WriteLine(order.CustomerName);
}

遅延読み込みの注意点

遅延読み込みは便利ですが、SQL がいつ実行されるか見えにくくなります。

たとえば、車一覧を取得して、それぞれの注文へアクセスするコードです。

List<Car> cars = await context.Cars.ToListAsync();

foreach (Car car in cars)
{
    Console.WriteLine(car.Orders.Count);
}

この場合、最初に車一覧を取る SQL が 1 回、各車の注文を取る SQL が車の件数分実行される可能性があります。
これを N+1 問題と呼びます。

関連データが必要だと分かっているなら、Include() や投影を使う方が安全です。

どれを選ぶべきか

使い分けの目安です。

最初から必要なら Include

詳細画面など、親と子が必ず必要な場合に向いています。

context.Cars.Include(car => car.Orders)

条件付きで必要なら明示的な読み込み

ユーザー操作や条件によって関連が必要になる場合に向いています。

context.Entry(car).Collection(car => car.Orders).LoadAsync()

画面表示なら投影

必要な列だけ取りたい場合に向いています。

context.Cars.Select(car => new CarListItem { ... })

遅延読み込みは慎重に

便利ですが、SQL の発行タイミングが見えにくくなります。
チーム開発や高負荷なアプリでは、意図しない N+1 問題の原因になることがあります。

まとめ

EF Core では、関連データの読み込み方法を選べます。
Include()、明示的な読み込み、遅延読み込みには、それぞれ向いている場面と注意点があります。

関連データは「とりあえず全部読む」ではなく、「画面や処理に必要な形だけ読む」と考えると、性能と可読性のバランスが取りやすくなります。
次回は、取得したデータの更新と削除を扱います。

C# .NET Entity Framework Core Include 関連データ 遅延読み込み
← [C#] EF Core でデータを問い合わせる [C#] EF Core で更新と削除を扱う →

Related Posts

  • [C#] EF Core でデータベースの準備と初期データを扱う Aug 8, 2026
  • [C#] EF Core とリポジトリでデータ操作を整理する Aug 7, 2026
  • [C#] DbContext の設定と保存処理を拡張する Aug 6, 2026
  • [C#] EF Core のエンティティと表示用モデルを設計する Aug 5, 2026

Table of Contents

  • サンプルモデル
  • 関連データは自動では常に読み込まれない
  • Include で一緒に読み込む
  • ThenInclude でさらに深い関連を読み込む
  • 投影で関連データを必要な形にする
  • 明示的に読み込む
  • 関連データに条件を付けて読み込む
  • 遅延読み込み
  • 遅延読み込みの注意点
  • どれを選ぶべきか
    • 最初から必要なら Include
    • 条件付きで必要なら明示的な読み込み
    • 画面表示なら投影
    • 遅延読み込みは慎重に
  • まとめ

Recent Posts

  • [C#] EF Core でデータベースの準備と初期データを扱う Aug 8, 2026
  • [C#] EF Core とリポジトリでデータ操作を整理する Aug 7, 2026
  • [C#] DbContext の設定と保存処理を拡張する Aug 6, 2026
  • [C#] EF Core のエンティティと表示用モデルを設計する Aug 5, 2026
  • [C#] EF Core を中心にデータアクセス層を分ける Aug 4, 2026

Categories

  • C#150
  • .NET149
  • AWS27
  • Laravel16
  • Entity Framework Core15
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • Data Access6
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1
  • Filament1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • Entity Framework Core
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • LINQ
  • System.Collections.Generic
  • デリゲート
  • リフレクション
  • ADO.NET
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メタデータ
  • メモリ管理
  • CIL
  • DoS
  • Nginx
  • SQL Server
  • WordPress
  • ラムダ式
  • 監視
  • 設計
  • Amazon Linux 2023
  • Delegate
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • LINQ to Objects
  • OPCache
  • Pointer
  • Reflection
  • System.Collections.Specialized
  • Unsafe
  • Webサーバー
  • アセンブリ
  • インターフェース
  • オブジェクト指向
Powered by Hugo & Explore Theme.