Entity Framework Core を使用して MySQL にアクセスする準備を行う (データベースファースト)

ページ作成日 :

動作確認環境

Visual Studio
  • Visual Studio 2022
.NET
  • .NET 8
Entity Framework Core
  • Entity Framework Core 8.0
MySQL
  • MySQL 8.4
Pomelo.EntityFrameworkCore.MySql
  • 8.0.2

※上記は検証環境ですがほかのバージョンでも動作する可能性はあります。

はじめに

Entity Framework Core は O/R マッパーのライブラリであり、データベースにアクセスする際に SQL 文を直接記述せずコードで定義されたモデル(クラス)を介してデータベースのレコードにアクセスできます。 これにより以下のようなメリットがあります。

  • SQL 文を直接記述しないので SQL インジェクションなどのセキュリティリスクを減らす
  • SQL 文は文字列であるため構文をミスしてもビルドエラーのチェック対象にならないがモデルはプログラム構文なのでビルド時にミスをチェックできる

Entity Framework Core ではこれらのモデルやデータベースへ接続するコードを既存のデータベースから自動的に生成したり、 逆にコードを手動で作成してからデータベースを自動で生成する方法が備わっています。

前者は「データベースファースト」と呼ばれ後者は「コードファースト」と呼ばれます。 ER 図のような設計図からコードやデータベースを生成する「モデルファースト」もありますが Entity Framework Core ではあまり使われていません。

今回はデータベースがすでにあるという前提でコードを生成する「データベースファースト」のパターンを使用します。

MySQL のセットアップ

本 Tips では MySQL のデータベースにアクセスするため事前に MySQL をセットアップしておいてください。 開発環境の PC、またはネットワーク経由でほかの PC にセットアップする形でも構いません。 開発環境から MySQL に接続できれば OK です。 本 Tips では別環境に MySQL をインストールしています。

MySQL のセットアップ手順については冗長になるため割愛します。 以下のページに MySQL 関連の Tips を掲載しているのでセットアップの手順を知りたい方は参照してください。

テーブルの作成

今回はサンプルとして以下のスキーマ(データベース)とテーブルを作成します。

  • スキーマ名 : test_schema
  • テーブル名 : user
  • テーブルの列 : [id], [name], [password], [age], [email], [birthday], [is_provisional_registration], [update_datetime]

どのような方法で作成しても問題ありませんが、面倒であれば以下の SQL を MySQL に対して実行して生成してください。

以下はスキーマ作成 SQL です。

CREATE DATABASE `test_schema`;

テーブル作成 SQL です。

CREATE TABLE `user` (
  `id` int NOT NULL,
  `name` varchar(20) NOT NULL,
  `password` varchar(20) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `email` varchar(200) DEFAULT NULL,
  `birthday` date DEFAULT NULL,
  `is_provisional_registration` tinyint(1) NOT NULL,
  `update_datetime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='テストコメント';

レコード追加 SQL です。

INSERT INTO `test_schema`.`user` (`id`,`name`,`password`,`age`,`email`,`birthday`,`is_provisional_registration`,`update_datetime`) VALUES (1, '氏名1', 'aaaa', 20, 'aaaa@example.com', '2020-04-01', 0, '2021-03-14T00:00:00.0000000');
INSERT INTO `test_schema`.`user` (`id`,`name`,`password`,`age`,`email`,`birthday`,`is_provisional_registration`,`update_datetime`) VALUES (2, '氏名2', 'bbbb', 30, 'bbbb@example.com', '2010-04-01', 1, '2021-03-14T00:00:00.0000000');

アカウントへの権限の設定

プログラムから指定したアカウントで MySQL のスキーマにアクセスできるようにホスト名を設定しておいてください。

また、接続するアカウントで test_schema にアクセスする権限を設定する必要があります。

今回レコードを取得する処理を実行するので SELECT の権限を付与します。 今回は行いませんが挿入や更新などを行いたい場合は INSERTUPDATE などにもチェックを入れてください。

Visual Studio のセットアップ

こちらもすでにセットアップしているものとします。 セットアップの手順などを知りたい場合は以下のページにまとめています。

プロジェクトの作成

Entity Framework Core は特定の実行環境に依存しているわけではないので多くのプロジェクトで使用することができます。 今回はシンプルなコンソールアプリケーションの環境で Entity Framework Core を使用してみます。

新しいプロジェクトで「コンソール アプリ」を選択します (.NET Framework ではない方です)。

プロジェクトを作成した状態です。プロジェクト名などはなんでも構いません。

MySQL 用の Entity Framework Core のパッケージを取得する

NuGet で Entity Framework Core を使用するためのパッケージを取得します。 パッケージは以下のものを使用します。こちらは Microsoft 公式のものではありませんが多く利用されています。

  • Pomelo.EntityFrameworkCore.MySql

依存関係を右クリックして「NuGet パッケージの管理」を選択します。

タブから「参照」を選択している状態で検索欄に Pomelo と入力します。すると一覧に関連するパッケージが表示されます。

この中から以下のパッケージをインストールします。

  • Pomelo.EntityFrameworkCore.MySql

インストール対象を選択して インストール ボタンをクリックします。バージョンは最新の安定版を選択します。

ダイアログは基本的に OK をクリックして問題ありません。

パッケージは以下のようになっていると思います。

続いて検索欄に EntityFrameworkCore と入力し、同様に以下をインストールしてください。

  • Microsoft.EntityFrameworkCore.Tools

バージョンについては Pomelo.EntityFrameworkCore.MySql が参照している Microsoft.EntityFrameworkCore のバージョンと合わせてください。 今回は 8.0.2 を参照しているので Microsoft.EntityFrameworkCore.Tools も 8.0.2 にしておきます。 こちらのパッケージはデータベースからコードを生成する為に使用します。

最終的には以下のようになっているはずです。

データベースのテーブル構成からモデル (コード) を作成する

モデルなどのコードを自動生成させるにはまずプロジェクトをビルドしてエラーがないことを確認します。 エラーがあるとモデルの作成に失敗します。すでにエラーがないことを確認済みであるのであればビルドする必要はありません。

Visual Studio から「パッケージ マネージャー コンソール」を開きます。 ない場合はメニューの「ツール」「NuGet パッケージ マネージャー」「パッケージ マネージャー コンソール」から開けます。

以下のようなウィンドウが表示されるので、右上の「既定のプロジェクト」がモデルを作成する対象のプロジェクトになっていることを確認します。 (複数プロジェクトを持っている場合に気を付ける必要があります)

入力欄に以下のテキストを入力します。環境によってパラメータが変わりますので、それぞれ以下の説明ともとに適時変えてください。

Scaffold-DbContext "Server=<サーバー名>; Database=<スキーマ名>; user=<接続ユーザー名>;password=<接続パスワード>;" Pomelo.EntityFrameworkCore.MySql -f -OutputDir "<出力フォルダパス>" -Context "<コンテキストクラス名>" -UseDatabaseNames -DataAnnotations -NoPluralize

入力例

Scaffold-DbContext "Server=TestServer; Database=test_schema; user=test_user;password=XXXXXXXX;" Pomelo.EntityFrameworkCore.MySql -f -OutputDir "Models\Database" -Context "TestDatabaseDbContext" -UseDatabaseNames -DataAnnotations -NoPluralize
パラメータ 説明 パラメータ例
Provider MySQL なら Pomelo.EntityFrameworkCore.MySql など指定します。 Pomelo.EntityFrameworkCore.MySql
Connection データベースに接続するための接続文字列です。接続文字列は他のアプリでも共通で使用できるので指定する内容については接続文字列の記法にしたがって記述してください。ちなみにモデルを作成するためだけに一時的に使用されるだけですので、この接続文字列についてはアプリ公開後のセキュリティなどを意識する必要はありません。パスワードに記号を入れている場合はエスケープに注意してください。 "Server=TestServer; Database=test_schema; user=test-user;password=XXXXXXXX;"
f すでにプログラムがあった場合でも強制的に上書きします。 <なし>
OutputDir コードを出力するフォルダパス。プロジェクトフォルダからの相対パス Models\Database
Context Entity Framework を使用する際のコンテキストクラス名 TestDatabaseDbContext
UseDatabaseNames 指定するとデータベースのテーブル名がそのままクラス名になります。指定しない場合は規則に従ってエンティティクラス名の大文字小文字が調整されます。 <なし>
DataAnnotations 指定すると列の型によって各プロパティに DataAnnotation 属性が自動で付加されます。データベースの型に沿って自動的に入力チェックを行いたい場合に少し便利です。 <なし>
Namespace 生成されるエンティティクラスが所属する名前空間。指定しない場合はフォルダに合わせて名前空間が決定される。 TestNamespace
ContextNamespace 生成される Context が所属する名前空間。指定しない場合はフォルダに合わせて名前空間が決定される。 TestNamespace
NoOnConfiguring コードの中に生の接続文字列を埋め込まないようにします。 <なし>
NoPluralize Context の各テーブル名のプロパティ名が複数形にならないようにします。 <なし>

Enter を押して実行すると以下のようになり、コードが自動生成されます。エラーになる場合はその理由が表示されるのでエラー内容に合わせて対応してください。

user テーブルのモデルコードは以下のようになっています。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace SetupMysqlDatabaseFirst.Models.Database;

/// <summary>
/// テストコメント
/// </summary>
public partial class user
{
  [Key]
  public int id { get; set; }

  [StringLength(20)]
  public string name { get; set; } = null!;

  [StringLength(20)]
  public string? password { get; set; }

  public int? age { get; set; }

  [StringLength(200)]
  public string? email { get; set; }

  public DateOnly? birthday { get; set; }

  public bool is_provisional_registration { get; set; }

  [Column(TypeName = "datetime")]
  public DateTime? update_datetime { get; set; }
}

ちなみに警告が表示されるのは、生成されたコンテキストクラスのコードに接続文字列がそのまま記載されるためです。 可能であれば接続文字列は別なところに保管して実行時に設定するようにすべきですが、今回は動作確認目的なのでそのままにしておきます。

レコードを取得して表示してみる

データベースのレコードにアクセスするためのコードはできたので試しにレコードを取得してコンソールに表示してみます。

Program.cs を開き以下のように修正してください。

using SetupMysqlDatabaseFirst.Models.Database;
using System.Text.Json;

Console.WriteLine("Hello, World!");

// データベースコンテキストのインスタンスを生成する
using var dbContext = new TestDatabaseDbContext();

// データベースから User 一覧を取得する
var users = dbContext.user.ToList();

// 取得した User 情報をコンソールに書き出す
foreach (var user in users)
{
  Console.WriteLine(JsonSerializer.Serialize(user));
}

自動生成した DbContext クラスを new で生成します。データベースの接続を自動的に破棄できるように using var で宣言しています。

dbContext には各モデルにアクセスするためのプロパティが生成されているので今回は user プロパティにアクセスすることによって User テーブルのレコードを操作することができます。 発行される SQL は内部的に自動生成されるので意識する必要はありません。

ここでは ToList 拡張メソッドを使用して user テーブルのレコードをすべて取得しています。

後は foreachJsonSerializer.Serialize メソッドを使用して user の情報をコンソールに表示しています。 前述の通り user テーブルの各列はプロパティとして宣言されているので個別に値を取り出すことも可能です。