WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

MySQL で Contoso University チュートリアル (CORE)

by WebSurfer 2021年10月19日 12:22

データベースに MySQL を利用して、ASP.NET Core MVC アプリを Micorsoft のチュートリアル「ASP.NET Core MVC と EF Core - チュートリアル シリーズ」に従って作成してみました。その際に気になったこと、憶えておいた方が良さそうなことを備忘録として残しておきます。

Contoso University アプリ

チュートリアルはデータベースに SQL Server (LocalDB) を使うことを前提に書かれています。それを MySQL に代えて、チュートリアルの「1. 開始するには」から「8. コンカレンシーの競合の処理」までの手順に従って実装しました。

この記事で使った MySQL サーバーは Windows 10 にインストールしたバージョン 8.0.19 です。詳しくは先の記事「MySQL をインストールしました(その 3)」を見てください。

以下に「1. 開始するには」から「8. コンカレンシーの競合の処理」の各手順において、気になったこと、憶えておいた方が良さそうなことを書いておきます。

一番気になっていたのは「8. コンカレンシーの競合の処理」の手順の楽観的同時実行制御の実装でしたが、モデルのプロパティの定義だけ変更すれば MySQL でも可能でした。その結果が上の画像です。

(1) 開始するには

この記事では、チュートリアルに書いてあるようなプロジェクトの新規作成はせず、先の記事「MySQL で Movie チュートリアル (CORE)」で作成した既存のプロジェクトをベースに使いました。

まず Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore を NuGet からインストールします。その他の必要な NuGet パッケージはベースに使った既存のプロジェクトにインストール済です。結果は以下のようになります。

NuGet パッケージ

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore の役割は "ASP.NET Core middleware for Entity Framework Core error pages. Use this middleware to detect and diagnose errors with Entity Framework Core migrations." だそうです。チュートリアルの「データベース例外フィルターを追加する」と関係があるようです (未確認ですが)。

Strtup.cs ファイルにある Startup クラスの ConfigureServices メソッドに SchoolContext を登録するコードを追加します。以下の通りです。

SchoolContext を登録

チュートリアルの手順「SchoolContext を登録する」に書いてあるコードは SQL Server 用で、これを MySQL 用に変更する必要がありますので注意してください。

次に appsettings.json ファイルに接続文字列を設定します。チュートリアルの例は LocalDB 用ですので MySQL 用に変更します。以下のような感じです (あくまで「感じ」です。MySQL の設定により異なります)。

接続文字列を設定

チュートリアルの「コントローラーとビューを作成する」の手順でスキャフォールディングを行う際、NuGet パッケージに Microsoft.EntityFrameworkCore.SqlServer が必要になるケースがありますので注意してください。エンティティクラスの定義によると思われます。ここ「(1) 開始するには」の手順では不要でしたが、下の「(6) 関連データの読み取り」では必要でした。

アプリを実行すると EF Code First の機能を使って、接続文字列で database=ContosoUniversity1 と指定した通り ContosoUniversity1 という名前のデータベースと、SchoolContext クラスで指定した Course, Enrollment, Student という名前のテーブルが生成されます。

さらにチュートリアルには初期データをシードするためのコードが含まれており、その通り実装すれば、最初にアプリを実行した時にコードに書かれた通り初期データが Course, Enrollment, Student テーブルに登録されます。

(2) 作成、読み取り、更新、削除の操作

チュートリアルある通りに実装してチュートリアル通りの結果が得られます。

(3) 並べ替え、フィルター、ページング、グループ化

チュートリアルある通りに実装してチュートリアル通りの結果が得られます。

(4) 移行

ここは Migration 操作を学ぶことを目的としているようです。

チュートリアルでは dotnet コマンドを使っていますが、Visual Studio を使っているならそのパッケージマネージャーコンソールを使った方が簡単だと思います。

NuGet パッケージ Microsoft.EntityFrameworkCore.Tool が必要ですが、上のステップ (1) の画像の通りインストール済みですので、このチュートリアルの操作にはパッケージマネージャーコンソールを使いました。

Drop-Database, Add-Migration, Update-Database という操作を行いますが、データベースが MySQL だから何か問題が出るということは、少なくともこのチュートリアルの操作ではありませんでした。(この後のセクション「(5) 複合データ モデルの作成」では 2 点問題がありましたが)

ベースに使った既存のプロジェクトには他の既存のコンテキストクラスが含まれています。そういう場合は、Drop-Database, Add-Migration, Update-Database 操作の際に、既存のコンテキストと区別するため、-Context SchoolContext オプションの追加が必要です。

(5) 複合データ モデルの作成

MySQL ではチュートリアルの通りにはできないということがありました。問題があったのは以下の 2 点です。

  1. Column 属性」のセクションで Student クラスの FirstMidName プロパティに付与した [Column("FirstName")] 属性。
  2. Department エンティティを作成する」のセクションで Department クラスの Budget プロパティに付与した [Column(TypeName = "money")] 属性。

いずれも Add-Migration の後の Update-Database コマンドで前者は NotImplementedException、後者は MySqlException がスローされ、MySQL のテーブルの変更・生成に失敗します。

前者は既存の Student テーブルの FirstMidName 列の名前を Migration 操作で FirstName という名前に変更しようというものです。NotImplementedException 例外がスローされるということは MySQL 用の Entity Framework にその実装がされてないということのようです。

後者は MySQL には TypeName = "money" で指定した money 型はないためのエラーです。

上記の操作に失敗で先に進めることができなくなったしまったので、やむを得ずゼロから作り直しました。Drop-Database で ContosoUniversity3 をドロップし、Add-Migration で生成された Migrations フォルダのファイルを削除し、チュートリアルに従ってコードの修正・追加を最後まで終わらせてから(途中で Migration 操作は行わないで)、最後に一気に Add-Migration, Update-Database コマンドでデータベースを生成しました。

問題となった Student クラスの FirstMidName プロパティに付与した [Column("FirstName")] 属性ですが、データベースを生成する最初の時点で付与しておけば指定した通り Student テーブルの当該列の名前は FirstName になります。チュートリアルのように最初に FirstMidName という列名で作って、後で Migration 操作で変更しようとすると、その操作に必要なメソッド等が実装されてないからか、NotImplementedException がスローされます。

Department クラスの Budget プロパティに付与した [Column(TypeName = "money")] 属性は [Column(TypeName = "decimal")] に変更が必要です。

チュートリアルの「移行を追加する」の手順に、Migrations フォルダに生成された ComplexDataModel クラスのコードに手を加えるように書かれていますが、それは先の操作でデータベースに作成済みの既存のレコードとの整合を取るためのもので、ゼロから一気に作るのであれば不要です。

(6) 関連データの読み取り

スキャフォールディング操作の際 NuGet パッケージに Microsoft.EntityFrameworkCore.SqlServer が必要になります。ちなみに「(1) 開始するには」の手順では不要でした。上の「(5) 複合データ モデルの作成」でいろいろ変更・追加したエンティティクラスの定義によると思われます。

そのため、最初のスキャフォールディング操作には失敗しますが、その際 Microsoft.EntityFrameworkCore.SqlServer が自動的に追加されますので、以下のようになっていることを確認し再度スキャフォールディングすれば成功すると思います。

NuGet パッケージの確認

上の画像の 1 番目から 4 番目のパッケージ間でのバージョンの不一致、ランタイムとのバージョンの不一致があるとスキャフォールディングに失敗することがありますので注意してください。

もう一つ、MySQL とは関係ないチュートリアルのミスですが、Courses Taught by Selected Instructor テーブルを表示する View のコードで selectedRow = "success"; となっていますが、selectedRow = "table-success"; にしないと Select しても背景色が変わらないので修正してください。

(7) 関連データの更新

チュートリアルある通りに実装してチュートリアル通りの結果が得られます。

(8) コンカレンシーの競合の処理

チュートリアルの手順では Department テーブルに rowversion (Transact-SQL) 列を追加し、それを使って楽観的同時実行制御を実装するというものです。

MySQL には rowversion はなく、それに代わるものとして timestamp というものがありますが同じではありません。.NET 側では rowversion は byte[] 型、timestamp は DateTime 型になるのですが、そのような違いがあっても Entity Framework による楽観的同時実行制御に使えるかが懸念したところです。

チュートリアルには Department クラスに byte[] 型の RowVersion プロパティを追加して Migration 操作でデータベースの Department テーブルに RowVersion 列を追加するよう書かれています。まずそこを MySQL ように変更する必要があります。

ググって見つけた記事 entity framework 6 mysql rowversion を参考に以下のプロパティ定義を使いました。

[Timestamp]
[ConcurrencyCheck]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime TimeStamp { get; set; }

timestamp では分解能が不十分という話もあるようですが、11.3.1 DATE、DATETIME、および TIMESTAMP 型によると "DATETIME または TIMESTAMP 値には、マイクロ秒 (6 桁) までの精度で後続の小数秒部分を含めることができます" とのことですので十分ではないかと思います。なので今回は上のプロパティ定義で実装してみました。

マイクロ秒以内で同時実行が行われるケースも考えなければならないとか、どうしても分解能が気になるという場合は Better way to implement a row version with EF Core and MySQL? に紹介されている方法もありそうです。

Add-Migration, Update-Database コマンドで MySQL の Department テーブルには以下のようにそれらしい形で datetime 型の Timestamp 列が追加されます。timestamp 型でないのがちょっと気になりますが。

Department テーブル

特に気になっていたのはスキャフォールディング機能を使って Department テーブルの CRUD 用のコントローラーとビューを生成したとき TimeStamp 列を使って同時実行制御を行う機能が含まれるかということです。

結果は、SQL Server の場合と同様に、同時実行で DbUpdateConcurrencyException 例外がスローされ楽観的同時実行制御ができるようになりました。この記事の一番上の画像がその結果です。

なお、プロパティ名をチュートリアルの RowVersion から TimeStamp に変えましたので、チュートリアルの「Edit メソッドを更新する」の Edit アクションメソッドのコードで RowVersion, rowVersion となっているところは TimeStamp, timeStamp に変更が必要です。型の指定も byte[] となっているところを DateTime に変更する必要があります。

Edit 用の View のコードも同様で、「Edit ビューを更新する」のコードで RowVersion, rowVersion となっているところは TimeStamp, timeStamp に変更が必要です。

Delete については View の <input type="hidden" asp-for="RowVersion" /> の RowVersion を TimeStamp に変更します。


チュートリアルにはこの先に「9. 継承」と「10. 高度なトピック」がありますが、それはまた次の機会にということで・・・

Tags: , , , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2021年10月  >>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar