Zenn
Open6

Entity Framework CoreでMariaDB/MySQLに接続する方法

idumistidumist

実現したいこと

  1. C#からMariaDB(≒MySQL)を操作したい。
  2. C#からDBを操作する方法はいくつかあるが、O/RMとしてEntity Framework Coreを用いる。
idumistidumist

前提条件

  • .NET 8
  • Entity Framework Core 8[1]
  • MariaDB 10.x (≒MySQL 5.7)
  • コンソールアプリで利用、今後ASP.NETやBlazor等でも利用したい
脚注
  1. .NET 8対応の最新版はEF Core 9だが、以下のPomeloのバージョンと合わせるため。 ↩︎

idumistidumist

結論

Pomelo.EntityFrameworkCore.MySqlを使う

https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql

MySQL最新版を利用しているなら、MySql.EntityFrameworkCoreもある。(MySql.Data.EntityFrameworkCoreはディスコン)
https://learn.microsoft.com/ja-jp/ef/core/providers/?tabs=dotnet-core-cli

idumistidumist

どのように進めるか

以下の3つの手法がある。

  1. 手動でDB側テーブルを作成し(または既存DBを参照し)、手動でモデルのコードを書く
  2. 手動でDB側テーブルを作成し(または既存DBを参照し)、自動でモデルのコードを生成する → スキャフォールディング
  3. 手動でモデルのコードを書き、自動でDB側テーブルを作成する → コードファーストアプローチ

今回は既存DBに接続したいので1か2になるが、良い機会なので先に3を試しておく。

idumistidumist

3. コードファーストアプローチを試す

以下のブログを参考にテストしてみる。とてもわかりやすい。ありがとうございます!
https://hirahira.blog/efcore-mysql/

コンテキストクラスのMySqlServerVersionMariaDbServerVersionに変更。Todoエンティティクラスはそのまま。この2つのクラスを1つのmodel.csファイルにまとめる。

model.cs
using Microsoft.EntityFrameworkCore;

namespace ConsoleApp1;

internal class TodoContext : DbContext
{
    public DbSet<Todo> Todos { get; set; }

    // 接続文字列
    readonly string connectionString = "server=10.0.0.2;database=todo;user=権限のある誰か;password=ここに埋め込まずに外に書く;";

    // MariaDBのバージョン
    //readonly MySqlServerVersion serverVersion = new(new Version(8, 0, 36));
    readonly MariaDbServerVersion serverVersion = new(new Version(10, x, y));

    // DBコンテキストの設定
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseMySql(connectionString, serverVersion);
}

internal class Todo
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
}

Pomelo.EntityFrameworkCore.MySqlはReleasedの最新版8.0.2。
Microsoft.EntityFrameworkCore.Toolsも最新版9.0.2をインストールしたところ、Add-Migration InitialCreateでエラーが発生した。

PM> Add-Migration InitialCreate
Build started...
Build succeeded.
Unable to create a 'DbContext' of type 'TodoContext'. The exception 'Method 'get_LockReleaseBehavior' in type 'Pomelo.EntityFrameworkCore.MySql.Migrations.Internal.MySqlHistoryRepository' from assembly 'Pomelo.EntityFrameworkCore.MySql, Version=8.0.2.0, Culture=neutral, PublicKeyToken=2cc498582444921b' does not have an implementation.' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
PM> 

EF Coreのドキュメントに

必ず、Microsoft から出荷されたすべての EF Core パッケージの同じバージョンをインストールしてください。[1]

と記載されていたので、Microsoft.EntityFrameworkCore.Tools 9.0.2をアンインストール、Pomeloが要求しているものと同じ8系の最新版8.0.13をインストールし直して再実行。

PM> Add-Migration InitialCreate
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
PM> Update-Database
Build started...
Build succeeded.
Applying migration '20250225054944_InitialCreate'.
Done.
PM> 

問題なさそう。事前にDB側でC#専用ユーザを作成して適切な権限付与を行っておけば、create databaseの実行すら不要で、コンテキストクラスで指定したtodoというdatabaseが作成されている。

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| todo               |
+--------------------+
2 rows in set (0.00 sec)

MariaDB [(none)]> use todo;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

MariaDB [todo]> show tables;
+-----------------------+
| Tables_in_todo        |
+-----------------------+
| Todos                 |
| __EFMigrationsHistory |
+-----------------------+
2 rows in set (0.00 sec)

MariaDB [todo]> show create table Todos\G
*************************** 1. row ***************************
       Table: Todos
Create Table: CREATE TABLE `Todos` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `Name` longtext,
  `IsComplete` tinyint(1) NOT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

MariaDB [todo]> show create table __EFMigrationsHistory\G
*************************** 1. row ***************************
       Table: __EFMigrationsHistory
Create Table: CREATE TABLE `__EFMigrationsHistory` (
  `MigrationId` varchar(150) NOT NULL,
  `ProductVersion` varchar(32) NOT NULL,
  PRIMARY KEY (`MigrationId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)

MariaDB [todo]> select * from __EFMigrationsHistory;
+------------------------------+----------------+
| MigrationId                  | ProductVersion |
+------------------------------+----------------+
| 20250225054944_InitialCreate | 8.0.2          |
+------------------------------+----------------+
1 row in set (0.00 sec)

PRIMARY KEYも設定されている。

慣例により、Id または <type name>Id という名前のプロパティがエンティティの主キーとして構成されます。[2]

この後、CRUD操作も問題なく動作した。

脚注
  1. https://learn.microsoft.com/ja-jp/ef/core/what-is-new/nuget-packages ↩︎

  2. https://learn.microsoft.com/ja-jp/ef/core/modeling/keys ↩︎

idumistidumist

1. 既存DBを参照し、手動でモデルのコードを書く

モデルのコードで必要なのは、DBとのセッションを表すコンテキストと、テーブルとのマップを示すエンティティ。

ただ、上記例のままではパスワード等がコード直書きとなっていて望ましくない。このような場合、一般的にappsettings.jsonが使われるようなので、コンソールアプリでも使えるようにする。

appsettings.jsonから値を取得する方法は以下を参考にする。
https://zenn.dev/higmasu/articles/b3dab3c7bea6db

appsettings.jsonの作成は以下を参考にする。(今回汎用ホストは設定しない)
https://qiita.com/jun1s/items/6b2b1b488258b942cd29

手順は以下の通り

  • プロジェクト直下に「JavaScript JSON 構成ファイル」を追加しファイル名をappsettings.jsonとする。
  • appsettings.jsonのプロパティで「出力ディレクトリにコピー」の項目を「コピーしない」から「常にコピーする」に変更する。(これでConsoleApp1.csprojに以下が反映される)
  <ItemGroup>
    <None Update="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>
  • appsettings.jsonconnectionStringを記載する
{
  "ConnectionStrings": {
    "connectionString": "server=10.0.0.2;database=todo;user=権限のある誰か;password=パスワード;"
  }
}
  • パッケージのMicrosoft.Extensions.Configuration 9.0.2とMicrosoft.Extensions.Configuration.Json 9.0.2を追加する。
  • appsettings.jsonを読み込むようmodel.csを変更する。
model.cs

ログインするとコメントできます