Core 3.1 ベース(.NET Framework ではありません)の ASP.NET MVC アプリで認証に ASP.NET Core Identity を用い、ユーザー情報のストアに MySQL を利用するにはどうするかということを書きます。
Visual Studio 2022 + .NET 6.0 の ASP.NET Core Identity の場合は「.NET 6.0 ASP.NET Identity に MySQL 使用 (CORE)」を見てください。
.NET Framework 版の ASP.NET Identity の場合は「ASP.NET Identity で MySQL 利用 (.NET 版)」を見てください。
ネットの情報ではサードパーティ製の Pomelo.EntityFrameworkCore.MySql を使ったという記事を目にしますが、ここでは NuGet でインストールできる Oracle 製の MySql.Data.EntityFrameworkCore v8.0.20 を使ってみました。
Oracle 製の MySql.Data.EntityFrameworkCore は Core に対応してないので下記ステップ「(6) プロジェクトのビルド」でエラーになったという話を聞きましたが、最新版ではそのあたりは対応されているようです。
ただし、何も問題がなかったわけではなく、最後に Update-Database でデータベース / テーブルを生成するところで "Specified key was too long; max key length is 3072 bytes" というエラーが出ます・・・が、解決は可能でした。詳しくは下のステップ「(8) Update-Database の実行」を見てください。
(1) プロジェクトの作成
Visual Studio 2019 のテンプレートを利用して ASP.NET Core 3.1 MVC アプリを作成します。認証は「認証なし」のままとしておきます。
認証に「個別のユーザーアカウント」を選んでプロジェクト作成時点から ASP.NET Identity を実装しても、それを MySQL を利用するように変更できると思います。しかし、そうすると Login, Register 他の認証関係の機能は Razor Class Library (RCL) として提供され、ソースコードはプロジェクトには含まれません。
認証関係のソースコードを見たい / 修正したいことがあるでしょうから(少なくとも登録、ログインページは書き換えたくなるはず)、まず「認証なし」で作って下のステップ (2) に述べるようにスキャフォールディング機能を利用して ASP.NET Identity を実装するのがよさそうです。(「個別のユーザーアカウント」で作って、特定のページをオーバーライドすることもできるそうですが)
(2) ASP.NET Core Identity の実装
Microsoft のドキュメント Scaffold identity into an MVC project without existing authorization を参考に、スキャフォールディング機能を利用して ASP.NET Identity を実装します。
ドキュメントの通り、ソリューションエクスプローラーでプロジェクトを右クリック⇒[追加(D)]⇒[新しいスキャフォールディングアイテム(F)...]で下の画像のダイアログが開きます。
上のダイアログで[インストール済み]の項目から[ID]を選択し、[追加]をクリックすると以下の画像の「ID の追加」ダイアログが表示されます。
レイアウトページ欄にはステップ (1) で生成したプロジェクトのレイアウトページ ~/Views/Shared/_Layout.cshtml を設定します。
ソースコードが必要なファイルに[オーバーライドするファイルの選択]でチェックを入れます。(上の画像では[すべてのファイルをオーバーライド]にチェックを入れて全てのファイルを選択しています。どれを選んだらよいか分からなかったので)
[データコンテキストクラス(D)]には、+ をクリックすると表示されるデフォルトをそのまま設定しました。必要なら任意の名前に変更できます。デフォルトでは xxxxxContext の xxxxx がプロジェクト名になり、プロジェクトルート下に Areas/Identity/Data というフォルダが作られ、その中にコンテキストクラスを含むファイル xxxxxContext.cs が生成されます。
[ユーザークラス(U)]にも同様に、+ をクリックすると表示されるデフォルトをそのまま設定しました。これも任意の名前に変更できます。デフォルトでは IdentityUser を継承した xxxxxUser クラスが生成され、xxxxx がプロジェクト名になります。
[ユーザークラス(U)]に何も設定せず空白のままにしておくと IdentityUser がそのまま使われます。基本的な機能はそれで問題ないですが、もし、将来プロファイル情報を追加するようなことがあると困ることになりますので、設定しておいた方がよさそうです。
その他、NuGet での Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore の追加、Razor ページを有効にするための Startup.cs へのコードの追加を忘れないようにしてください。詳しくは上に紹介した Microsoft のドキュメントを見てください。
(3) MySql.Data.EntityFrameworkCore
NuGet で MySql.Data.EntityFrameworkCore をインストールします。この記事を書いた時点では v8.0.20 しか見つからなかったのでそれをインストールしました。
2021/11/26 追記: MySql.Data.EntityFrameworkCore はいつの間にか非推奨になってました。 代替えパッケージが MySql.EntityFrameworkCore とのことです。それを使って同様なことができるかは未検証・未確認です。
関連するパッケージの更新・インストールが自動的に行われます。その内容は以下の画像を見てください。
(4) 接続文字列の変更
テンプレートで自動生成された接続文字列は appsetteins.json にありますが、それは LocalDB を利用するように設定されていますので MySQL に接続するように変更します。以下のような感じです。
例えば、上の画像の database=coreidentity というようにデータベース名を指定すると、Entity Framework Code First の機能を使って coreidentity という名前のデータベースを新たに生成し、そこに必要なテーブルを生成してくれます。(データベース名は任意に設定できます。この記事で coreidentity としたのは単なる例です)
(5) IdentityHostingStartup.cs の修正
自動生成されれた Areas/Identity/IdentityHostingStartup.cs ファイルで、サービス登録のコードが SQL Server を使うように設定されていますが、これを MySQL を使うように変更します。以下のような感じです。
なお、IdentityHostingStartup.cs ファイルは、上のステップ (2) のようにス���ャフォールディング機能を利用して ASP.NET Identity を実装した場合に生成されるものです。プロジェクト作成の時点で認証を「個別のユーザーアカウント」とした場合は、サービス登録のコードは Startup.cs ファイルに含まれますので注意してください。
(6) プロジェクトのビルド
ここで一旦プロジェクトをビルドしてみました。以下の通り成功します。
参考にさせていただいた記事「ASP.NET CoreでMySQLを利用する」ではここでエラーとなったそうです。
その記事では Core のバージョンは 2.0、MySql.Data.EntityFrameworkCore のバージョンは 8.0.6 だったそうです。この記事では Core は 3.1、MySql.Data.EntityFrameworkCore は 8.0.20 です。その差があるのでしょうか。
(7) Add-Migration の実行
パッケージマネージャーコンソールから Add-Migration CreateIdentitySchema を実行します(CreateIdentitySchema という名前は任意です。また、Core では Enable-Migration は不要になったそうです)。
Migrations フォルダには CreateIdentitySchema.cs ファイルが生成されます。ざっと見たところ内容は正しそうです。(ツールのバージョンが 3.1.2 でランタイムのバージョン 3.1.4 より古いという警告は出ていますが Migration には影響ないようです)
(8) Update-Database の実行
次に Update-Database を実行し、Entity Framework Code First の機能を利用して MySQL にデータベース / テーブルの生成を試みます・・・が、以下の画像の通りダメでした。
メッセージを見ると AspNetUserRoles というテーブルを生成する際に "Specified key was too long; max key length is 3072 bytes" という制約のためエラーになったということのようです。
以下、少々話が長くなりますが、なぜそういうエラーになるかと、その解決方法を書きます。
データベース / テーブルは CreateIdentitySchema.cs ファイルのコードに基づいて生成されるのですが、主キーは全て、
UserId = table.Column<string>(nullable: false)
というというような maxLength が指定されない設定となります。その場合、上の画像にある通り、テーブルを生成する create 句では主キーは varchar(767) に設定されます。
varchar(767) の 767 という数字は MySQL の主キーに許容される最大バイト数で、それゆえ主キーに maxLength が指定されないと varchar(767) になるのだと思われます。
767 バイトという制限は、「InnoDBインデックスの最大キー長について」という記事によると、ある条件で 3,072 バイトまで拡張できるそうで、MySQL 5.7 以降がその条件に当てはまるそうです。この記事では MySQL 8.x を使っているのでエラーメッセージにある通り 3,072 バイトが上限になっているのだと思います。
何故 nvarchar ではなくて varchar になるのか不思議でしたが、MySQL のドキュメント 10.3.7 The National Character Set によると、"MySQL uses utf8 as this predefined character set" なのでどちらでも同じなのだそうです。ちなみに create 句で nvarchar と指定しても、生成されるのは varchar になるそうです。
この記事で使用している MySQL 8.0.19 で使用されている文字コードを確認してみましたが、以下のようになっていました。
utf8mb4 というのは、10.1.10.7 utf8mb4 文字セット (4 バイトの UTF-8 Unicode エンコーディング) によると、"utf8 という名前の文字セットは、文字あたり最大 3 バイトを使用し、BMP 文字だけを含みます。utf8mb4 文字セットは、文字ごとに最大 4 バイトを使用し、補助文字をサポートします" ということだそうです。
utf8mb4 を使用していますので、主キーに設定された varchar(767) は 767 x 4 = 3,068 バイトになります。制限の 3,072 より小さいので単独の主キーなら varchar(767) で問題ないはずです。
しかし、ASP.NET Core Identity が使うテーブルには連結主キーが設定されるものがあり、それが最大 3,072 バイトの制約を超えるため "Specified key was too long; max key length is 3072 bytes" というエラーになったということのようです。
連結主キーが設定されるテーブルは AspNetUserRoles と AspNetUserTokens です。
それらのテーブルの主キーが varchar(767) とならないよう、CreateIdentitySchema.cs ファイルの当該コードに制限 maxLength: 128 を設定してやります。以下の画像の赤枠部分を見てください。
Id フィールドには d85de5ae-a926-45cd-831a-4835034f6b79 というように書式指定された Guid の文字列が設定されますので、varchar(128) で十分なはずです(少なくとも現時点では)。
上の画像のように maxLength: 128 を設定後 Update-Database を実行すれば MySQL に ASP.NET Core Identity に必要なデータベースとテーブルが生成されます。
その後で Visual Studio から MVC アプリを動かしてユーザー登録できます。登録したユーザーはデーターベースに反映されます。もちろん登録した ID とパスワードでアプリにログインできるようになります。