bucket-sort logo bucket-sort

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

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] EF Core でデータベースの準備と初期データを扱う

[C#] EF Core でデータベースの準備と初期データを扱う

Aug 8, 2026 C# , .NET , Entity Framework Core bucket-sort

前回は、EF Core とリポジトリを組み合わせて、データ操作の入口を整理しました。

今回は、データベースそのものの準備を扱います。
開発中やテストでは、次のような処理がよく必要になります。

  • データベースを削除して作り直す
  • マイグレーションを適用する
  • サンプルデータを投入する
  • テストごとにデータを初期状態へ戻す

これらを毎回手作業で行うと、環境差や手順漏れが起きやすくなります。
プログラムから安全に扱える形にしておくと、開発体験が大きく良くなります。

データベースを作り直す

EF Core には、データベースを削除したり作成したりする API があります。

public sealed class DatabaseInitializer
{
    private readonly AutoLotContext _context;

    public DatabaseInitializer(AutoLotContext context)
    {
        _context = context;
    }

    public async Task RecreateAsync()
    {
        await _context.Database.EnsureDeletedAsync();
        await _context.Database.EnsureCreatedAsync();
    }
}

EnsureDeletedAsync() はデータベースを削除します。
EnsureCreatedAsync() は、現在のモデルからデータベースを作成します。

この方法は簡単ですが、マイグレーション履歴を使いません。
開発用の一時データベースや、学習用のサンプルでは便利です。

一方で、マイグレーションで管理している本番相当のデータベースでは、次の方法を使う方が自然です。

マイグレーションを適用する

マイグレーションを使う場合は、MigrateAsync() を呼び出します。

public async Task ApplyMigrationsAsync()
{
    await _context.Database.MigrateAsync();
}

これにより、未適用のマイグレーションが順番に適用されます。

アプリケーション起動時に実行するなら、次のようにスコープを作って呼び出せます。

using var scope = app.Services.CreateScope();

var initializer = scope.ServiceProvider
    .GetRequiredService<DatabaseInitializer>();

await initializer.ApplyMigrationsAsync();

ただし、本番環境でアプリケーション起動時に自動適用するかどうかは、運用方針によります。
大きな変更や長時間のロックが発生する可能性があるため、本番では事前にスクリプトを確認して適用する運用も多いです。

開発環境だけで実行する

危険な初期化処理は、環境を見て制限します。

if (app.Environment.IsDevelopment())
{
    using var scope = app.Services.CreateScope();

    var initializer = scope.ServiceProvider
        .GetRequiredService<DatabaseInitializer>();

    await initializer.RecreateAsync();
    await initializer.SeedAsync();
}

削除を伴う処理を本番環境で実行しないことが重要です。
環境名だけに頼らず、接続先データベース名を確認するなど、二重の安全策を入れることもあります。

private void ThrowIfProductionDatabase()
{
    var connection = _context.Database.GetDbConnection();

    if (connection.Database.Contains("Production", StringComparison.OrdinalIgnoreCase))
    {
        throw new InvalidOperationException(
            "本番データベースに対して初期化処理は実行できません。");
    }
}

開発用の便利処理ほど、誤実行への備えが必要です。

サンプルデータを作る

初期データ投入では、まずデータを作る処理を分けておくと読みやすくなります。

private static List<Car> CreateCars()
{
    return new List<Car>
    {
        new()
        {
            Make = "Toyota",
            Color = "Blue",
            PetName = "Aqua",
            DateBuilt = new DateTime(2022, 4, 1),
            IsDrivable = true
        },
        new()
        {
            Make = "Honda",
            Color = "White",
            PetName = "Snow",
            DateBuilt = new DateTime(2021, 9, 12),
            IsDrivable = true
        },
        new()
        {
            Make = "Ford",
            Color = "Black",
            PetName = "Night",
            DateBuilt = new DateTime(2020, 11, 5),
            IsDrivable = false
        }
    };
}

投入処理です。

public async Task SeedAsync()
{
    if (await _context.Cars.AnyAsync())
    {
        return;
    }

    var cars = CreateCars();

    await _context.Cars.AddRangeAsync(cars);
    await _context.SaveChangesAsync();
}

すでにデータがある場合は何もしないようにしています。
開発用データを何度も重複投入しないためです。

関連データも一緒に投入する

関連データを持つ場合は、オブジェクトグラフとして作ると自然です。

private static List<Car> CreateCarsWithOrders()
{
    return new List<Car>
    {
        new()
        {
            Make = "Toyota",
            Color = "Blue",
            PetName = "Aqua",
            DateBuilt = new DateTime(2022, 4, 1),
            Orders =
            {
                new Order
                {
                    CustomerName = "佐藤",
                    OrderDate = new DateTime(2026, 8, 1)
                },
                new Order
                {
                    CustomerName = "田中",
                    OrderDate = new DateTime(2026, 8, 2)
                }
            }
        },
        new()
        {
            Make = "Honda",
            Color = "White",
            PetName = "Snow",
            DateBuilt = new DateTime(2021, 9, 12),
            Orders =
            {
                new Order
                {
                    CustomerName = "鈴木",
                    OrderDate = new DateTime(2026, 8, 3)
                }
            }
        }
    };
}

Car を追加すると、関連する Order も追加対象になります。

public async Task SeedWithOrdersAsync()
{
    if (await _context.Cars.AnyAsync())
    {
        return;
    }

    await _context.Cars.AddRangeAsync(CreateCarsWithOrders());
    await _context.SaveChangesAsync();
}

サンプルデータは、実際の画面で起こりそうな状態を含めると役立ちます。
正常なデータだけでなく、整備中、注文なし、複数注文あり、古いデータなどを混ぜると、画面や検索条件の確認がしやすくなります。

既存データを消してから入れ直す

テストや開発では、毎回同じ初期状態に戻したいことがあります。

public async Task ResetSampleDataAsync()
{
    _context.Orders.RemoveRange(_context.Orders);
    _context.Cars.RemoveRange(_context.Cars);

    await _context.SaveChangesAsync();

    await _context.Cars.AddRangeAsync(CreateCarsWithOrders());
    await _context.SaveChangesAsync();
}

削除順に注意が必要です。
外部キー制約がある場合、子テーブルを先に削除しないと失敗することがあります。

大量データを扱う場合は、RemoveRange() よりも一括削除や SQL を使う方が効率的なこともあります。

await _context.Orders.ExecuteDeleteAsync();
await _context.Cars.ExecuteDeleteAsync();

ExecuteDeleteAsync() はデータベース側で削除を実行します。
追跡中のエンティティとは同期されないため、同じ DbContext で続けて処理する場合は注意します。

テスト用の基底クラス

統合テストでは、データベース準備を共通化すると楽になります。

public abstract class DatabaseTestBase : IAsyncLifetime
{
    protected AutoLotContext Context { get; private set; } = default!;

    public async Task InitializeAsync()
    {
        var options = new DbContextOptionsBuilder<AutoLotContext>()
            .UseSqlServer(
                "Server=(localdb)\\mssqllocaldb;Database=AutoLot_Test;Trusted_Connection=True;")
            .Options;

        Context = new AutoLotContext(options);

        var initializer = new DatabaseInitializer(Context);
        await initializer.RecreateAsync();
        await initializer.SeedAsync();
    }

    public async Task DisposeAsync()
    {
        await Context.DisposeAsync();
    }
}

テストクラスは、この基底クラスを継承します。

public sealed class CarRepositoryTests : DatabaseTestBase
{
    [Fact]
    public async Task GetAvailableCarsAsync_ReturnsOnlyDrivableCars()
    {
        var repository = new CarRepository(Context);

        var cars = await repository.GetAvailableCarsAsync();

        Assert.All(cars, car =>
        {
            Assert.Equal("販売可能", car.Status);
        });
    }
}

このように、テストごとに同じ状態から始められると、失敗原因を追いやすくなります。

初期化処理はアプリケーションの品質に効く

データベース初期化は、地味な処理です。
しかし、開発者が何度も使う処理でもあります。

初期化が安定していると、次のような利点があります。

  • 新しい開発者が環境を作りやすい
  • テストデータの状態がそろう
  • 画面確認のたびに手作業でデータを直さなくてよい
  • マイグレーションの問題を早く見つけやすい
  • サンプルデータが仕様の説明にもなる

データアクセス層は、問い合わせや保存だけではありません。
データベースをどう準備し、どう育て、どう戻せるようにするかも大事な役割です。

今回で、EF Core を使ったデータアクセス層の基本的な構成を一通り見ました。
プロジェクト分割、モデル設計、DbContext の拡張、リポジトリ、初期化処理を組み合わせることで、アプリケーションは少しずつ保守しやすい形になります。

C# .NET Entity Framework Core Migration 初期データ テストデータ
← [C#] EF Core とリポジトリでデータ操作を整理する

Related Posts

  • [C#] EF Core のコマンドライン操作とマイグレーションを理解する Jul 28, 2026
  • [C#] EF Core とリポジトリでデータ操作を整理する Aug 7, 2026
  • [C#] DbContext の設定と保存処理を拡張する Aug 6, 2026
  • [C#] EF Core のエンティティと表示用モデルを設計する Aug 5, 2026

Table of Contents

  • データベースを作り直す
  • マイグレーションを適用する
  • 開発環境だけで実行する
  • サンプルデータを作る
  • 関連データも一緒に投入する
  • 既存データを消してから入れ直す
  • テスト用の基底クラス
  • 初期化処理はアプリケーションの品質に効く

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.