Entity Framework Core でコードファースト

プログラミング
公開 2022年4月20日

最小構成でコードファースト

SQL Server を使用する例です。

1パッケージをインストール

コンソールアプリを新規作成し、2つのパッケージをインストールします。

Install-Package Microsoft.EntityFrameworkCore.Tools
Install-Package Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
    パッケージマネージャーコンソールで Entity Framework のコマンドを実行するために使用します。
  • Microsoft.EntityFrameworkCore.SqlServer
    SQL Server を使うためのプロバイダーライブラリです。

2DbContext の作成

DbContext を継承したクラスを作成すれば Entity Framework が自動検出しますので、コードファーストを実行できます。
プログラムのエントリポイントで何らかのコードを書く必要はありません。
ただし、作成すべきテーブルが無いと事実上何も実行されないので、Hoge クラスを作成して DbSet<Hoge> 型のプロパティを DbContext を継承したクラスに追加します。

public class AppDbContext : DbContext
{
    public DbSet<Hoge> Hoge { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlServer("<接続文字列>");
}

public class Hoge
{
    public int Id { get; set; }
    public string Name { get; set; }
}

3マイグレーションを作成

  • プロジェクトをビルド
  • コンソールアプリをスタートアッププロジェクトに設定
  • パッケージマネージャコンソールを開いて、既定のプロジェクトにコンソールアプリを選択

この条件を満たしていることを確認したら、以下のコマンドを実行します。

Add-Migration hoge

以下のように出力されればOKです。

PM> Add-Migration hoge
Build started...
Build succeeded.
To undo this action, use Remove-Migration.

自動生成されたソースファイルが表示されます。ファイル名は yyyyMMddHHmmss_<マイグレーション名>.cs というファイル名になります。このファイルは Migrations という自動作成されたフォルダに作成されます。この C# のコードがデータベース変更を定義するソースコードになります。

4マイグレーションをデータベースに反映

次に以下のコマンドを実行します。

Update-Database

以下のように出力されれば反映完了です。

PM> Update-Database
Build started...
Build succeeded.
Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations-lock for more information if this takes too long.
Applying migration 'yyyyMMddHHmmss_hoge'.
Done.

実際にデータベースに接続してみると Hoge テーブルが作成されていることを確認できます。

Web アプリケーションの場合

コンソールアプリと同様に単一のプロジェクトでもコードファーストは使用できますが、以下のようにプロジェクトを分割しておくのがよいです。

  • (A) ASP.NET Core アプリケーション
  • (B) データアクセス用ライブラリ
  • (C) モデル(テーブル)定義

プロジェクトの依存関係は以下のようにします。
(A) ⇒ (B)、(C)
(B) ⇒ (C)

単一プロジェクトで実装する場合は、以下のそれぞれのコードを一つのプロジェクトにそのまま作成すればOKです。

(A) ASP.NET Core アプリケーション

Razor PagesMVC どちらでも方法は変わりません。

  • スタートアッププロジェクト
  • Program.cs(DbContext の DI 設定を追加)
  • appsettings.json(接続文字列)

例)DbContext の追加(PostgreSQL)

// Program.cs
var builder = WebApplication.CreateBuilder(args);

// DbContext の DI 設定を追加
builder.Services.AddDbContext<DefaultDbContext>(options =>
{
    options.UseNpgsql(builder.Configuration.GetDefaultConnectionString());
    options.EnableSensitiveDataLogging();
});

例)接続文字列

// appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Database=timecard;Username=postgres;Password=hoge;Port=5432"
  },

(B) データアクセス用ライブラリ

  • Migrations(*.cs)自動生成
  • DefaultDbContext.cs
  • DefaultDbContext.DbSet.cs
  • DefaultDbContext.OnModelCreating.cs
  • データベースプロバイダ(使用するデータベースに応じて選択)
    • Microsoft.EntityFrameworkCore.SqlServer
    • Microsoft.EntityFrameworkCore.Sqlite
    • Npgsql.EntityFrameworkCore.PostgreSQL
    • Oracle.EntityFrameworkCore

DbContext のクラスは partial にして用途別に分けておくと何かとメンテしやすいです。

例)DefaultDbContext(PostgreSQL)

// DefaultDbContext.cs
public partial class DefaultDbContext : DbContext
{
    public DefaultDbContext(DbContextOptions options)
        : base(options)
    {
        AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
    }
}

// DefaultDbContext.DbSet.cs
public partial class DefaultDbContext
{
    public DbSet<User> User { get; set; }
    public DbSet<UserCard> UserCard { get; set; }
    public DbSet<TimeCard> TimeCard { get; set; }
}

// DefaultDbContext.OnModelCreating.cs
public partial class DefaultDbContext
{
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<User>(e => { e.HasKey(e => new { e.UserId }); });
        builder.Entity<UserCard>(e => { e.HasKey(e => new { e.UserId, e.CardNumber }); });
        builder.Entity<TimeCard>(e => { e.HasKey(e => new { e.UserId, e.Date }); });

        // 名称を SnakeCase に変換
        builder.ToSnakeNames();
    }
}

(C) モデル(テーブル)定義

  • Models/Entities(*.cs)

テーブルとなるモデルクラスをここに作成します。

(A) の ViewModel/PageModel: 画面表示項目の定義
(C) のモデル: データベースのテーブル定義

このように役割を完全に分離させ、依存関係を (a) ⇒ (c) とすることで、画面表示の仕様変更があっても、データベースのテーブル定義が直接影響を受けないようにすることがポイントになります。

Migration の作成とデータベースへの反映

  1. モデルの新規作成/修正
  2. ソリューションのビルド
  3. パッケージマネージャーコンソールを表示
  4. パッケージマネージャーコンソールの「既定のプロジェクト」に「(B) データアクセス用ライブラリ」が選択されていることを確認
  5. スタートアッププロジェクトに「(A) ASP.NET Core アプリケーション」が選択されていることを確認
  6. 以下のコマンドを実行
Add-Migration <マイグレーション名>

<マイグレーション名> は任意ですが、Migrations フォルダ内で一意である必要があります。1

このタイミングで (B) データアクセス用ライブラリ/Migrations フォルダに変更内容を定義した *.cs ファイルが作成されます。

  1. 以下のコマンドを実行します。
Update-Database

モデルの変更がデータベースに適用されます。

脚注

  1. ファイル名は yyyyMMddHHmmss_<マイグレーション名>.cs になりますが、コード内のクラス名にはタイムスタンプが付きません。モデルの変更をデータベースに適用するごとに Add-Migration を実行することになりますが、同じ <マイグレーション名> は複数回使えないので、マイグレーション名の命名ルールをあらかじめ決めておいたほうがよいです。