WebSurfer's Home

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

PlanetScale を使ってみました

by WebSurfer 2023年2月15日 19:11

ASP.NET Core MVC アプリで PlanetScale をデーターベースに使ってみました。紆余曲折がありましたが何とか接続して使えるようになった結果が下の画像です。

PlanetScale を使ったアプリ

その紆余曲折と言うか、アプリの作成で自分が遭遇した問題は以下の通りです。

  1. EF Code First でテーブルを作成できない。
  2. 既存の DB からリバースエンジニアリングができない。
  3. BeginTransaction メソッドが使えない。

その顛末を以下に備忘録として書いておきます。上の各項目についても説明します。

PlanetScale とは何かですが、"PlanetScale is the world’s most advanced serverless MySQL platform" ということだそうで、要するに MySQL を使ったクラウド上のデータベースサーバーらしいです。制約として外部キー制約がつけられないということがあるそうです。

利用するにあたって、まず、PlanetScale にサインインしてデータベース名と地域を設定し、データベースを作成する必要があります。以下の画像がその設定の例です。

データベース名と地域を設定

データベースの作成に成功すると、以下のように自分が作成したデーターベース情報が表示されます。

自分の DB 情報

上の画像の[Connect]ボタンをクリックすると、下の画像のように接続情報が表示されます。Username と Password が表示されるのは初回のみなので必ず記録しておいてください

接続情報

さらに、上の画像の赤枠の[.NET]を選択すると ASP.NET Core アプリの appsettings.json に設定するための接続文字列の例が表示されますので、これも忘れないように記録しておくことをお勧めします。

以上はデータベース作成時からデフォルトで存在している main ブランチでの話です。このあと開発作業用にブランチを追加するのが普通ということなので自分もそうしてみました。気をつけなければならないのは追加した開発作業用ブランチへの接続用の Username と Password は main ブランチのものとは異なることです。

自分はそれを知らなくて半日ぐらいハマりました。開発作業用ブランチを作ってそれにテーブルを作成し、アプリからアクセスして SELECT クエリを投げたのですが、MySqlException がスローされてそのテーブルは見つからないと言われます。原因は Username と Password に main ブランチのものを使っていたので main ブランチに接続され、main ブランチにはそのテーブルは存在しないからでした。開発作業用ブランチに接続するには、開発作業用ブランチの Username と Password を使用する必要があるようです。

以上で PlanetScale 側のデータベースの準備は完了したはずなので、Visual Studio 2022 のテンプレートを使って .NET 6.0 の ASP.NET Core MVC のプロジェクトを新規に作成し、それから PlanetScale を使ってみます。

(1) EF Code First でテーブルを作成できない

最初に EF Code First の機能を使って PlanetScale にアプリが使うテーブルを作成してみます。先の記事「MySQL で Movie チュートリアル (CORE)」に書いた手順で、EF Code First の機能を利用して PlanetScale に Movie テーブルの作成をトライしてみました。

Add-Migration では問題なく Migration のためのクラスファイルが作成されます。しかし、Update-Database でのテーブルの作成に失敗します。

プロバイダに MySql.EntityFrameworkCore 6.0.10 を使った場合は、Update-Database の実行で KeyNotFoundException がスローされて失敗します。

Pomelo.EntityFrameworkCore.MySql 6.0.2 を使った場合は、ALTER DATABASE CHARACTER SET utf8mb4 というコマンドで失敗し "alter database is not supported" というエラーメッセージが出て、テーブルの作成に失敗します。

.NET 7.0 でも試してみましたが同じエラーで失敗します。

(2) リバースエンジニアリングができない

解決方法が分からないので、EF Code First による Movie テーブルの作成は諦めて、PlanetScale のサイトにアクセスして Console から手動で Movie テーブルを作成しました。下の画像の movie というのがそれです。

Console から手動で Movie テーブル作成

(注: __EFMigrationsHistory というテーブルもありますが、それは上に書いた EF Code First の操作でできたものです。その時は肝心の Movie テーブルは作成されていません)

先に作ったプロジェクトは放置して新たにゼロから ASP.NET Core MVC のプロジェクトを作成し、プロバイダには Pomelo.EntityFrameworkCore.MySql 6.0.2 を使って、リバースエンジニアリングでコンテキストクラスとエンティティクラスを作成をトライしました。

エラーなしで完了するもののコンテキストクラスしか生成されずその内容も不完全です。エンティティクラスは全く作成されません。

ちなみに、プロバイダに MySql.EntityFrameworkCore 6.0.10 を使った場合は KeyNotFoundException がスローされて何も生成されずに終わってしまいます。

これでは先に進めないので、不完全なコンテキストクラスは手直しして、エンティティクラスは自分でコードを書いて追加しました。その内容は以下の通りです。

コンテキストクラス

using Microsoft.EntityFrameworkCore;
using PlanetScaleMovie2.Models;

namespace PlanetScaleMovie2.Data
{
    public partial class myplanetscaleContext : DbContext
    {
        public myplanetscaleContext()
        {
        }

        public myplanetscaleContext(DbContextOptions<myplanetscaleContext> options)
            : base(options)
        {
        }

        // 以下の一行だけは自動生成されないので手動で追加
        public virtual DbSet<Movie> Movies { get; set; } = null!;

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // 自動生成されたコードは不要なので削除
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.UseCollation("utf8mb4_0900_ai_ci")
                .HasCharSet("utf8mb4");

            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }
}

エンティティクラス

using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;

namespace PlanetScaleMovie2.Models
{
    [Table("movie")]
    public partial class Movie
    {
        [Key]
        public int Id { get; set; }
        [Column(TypeName = "text")]
        public string? Title { get; set; }
        [Column(TypeName = "datetime")]
        public DateTime ReleaseDate { get; set; }
        [Column(TypeName = "text")]
        public string? Genre { get; set; }
        public decimal Price { get; set; }
    }
}

そのあとスキャフォールディング機能を使って Controller と View を作成します。

Controller と View の作成

Program.cs でコンテキストクラスを DI コンテナに登録します。

var serverVersion = new MySqlServerVersion(new Version(8, 0, 32));
var coonecctionString = builder.Configuration.GetConnectionString("PlanetScaleMovieContext");
builder.Services.AddDbContext<myplanetscaleContext>(options =>
    options.UseMySql(coonecctionString, serverVersion));

appsettings.json に上のコー��の "PlanetScaleMovieContext" という名前で接続文字列を追加します。Movie テーブル を開発作業用ブランチに作った場合は、接続文字列の user と password は開発作業用ブランチ用のものにすることに注意してください。

以上でアプリは今度は問題なく動きました。その結果がこの記事の一番上の画像です。

(3) BeginTransaction メソッドが使えない

これはプロバイダに Oracle 製の MySql.EntityFrameworkCore を使った場合です。以下のように BeginTransaction メソッドの行で KeyNotFoundException がスローされます。

BeginTransaction メソッド

プロバイダに Pomelo.EntityFrameworkCore.MySql を使った場合は問題ありません。Oracle 製はどうも相性が良くないようです。

なお、上記 (1), (2) の件については planetscale / discussionDoes PlanetScale support the .NET 6/7 EF Code First and reverse engineering? という質問を出しました。解決に向けて進展がありましたらこの記事に追記します。

Tags: , , ,

CORE

.NET 6.0 ASP.NET Identity に MySQL 使用 (その2)

by WebSurfer 2023年2月12日 16:15

.NET 6.0 の ASP.NET MVC アプリで、プロバイダに Oracle 製 MySql.EntityFrameworkCore v6.0.10 を使って、ASP.NET Core Identity のユーザー情報のストアに MySQL を利用する話を書きます。

ASP.NET Identity に MySQL 使用

先の記事「.NET 6.0 ASP.NET Identity に MySQL 使用 (CORE)」では、その時点で Oracle 製 MySql.EntityFrameworkCore は v5.0.8 までしかリリースされていなかったので、プロバイダには Pomelo.EntityFrameworkCore.MySql を使用しています。

その後 Oracle 製 MySql.EntityFrameworkCore の .NET 6.0 用がリリースされたので試してみましたが、v6.0.7 までは Add-Migration に失敗して使えませんでした。

先日バージョンアップの状況を調べてみたら v6.0.10 がリリースされていたので試してみたら Add-Migration は成功するようになってました。その後 Update-Database でデータベースは生成され、Resister でのユーザー登録、Login でのユーザー認証も問題なくできます。(ただし、100% 完全に動くかどうかまでは未検証ですが)

と言うわけで、プロバイダに Oracle 製 MySql.EntityFrameworkCore v6.0.10 を使ってのアプリの作り方を、先の記事の Pomelo.EntityFrameworkCore.MySql と違う点だけ以下に書いておきます。

(1) NuGet パッケージ

NuGet で MySql.EntityFrameworkCore v6.0.10 をインストールします。他に必要なものとバージョンを合わせました。

MySql.EntityFrameworkCore

(2) Program.cs の修正

自動生成されれた Program.cs ファイルで、サービス登録のコードが SQL Server を使うように設定されているはずですが、これを MySQL を使うように変更します。以下のような感じです。

Program.cs の修正

(3) Add-Migration / Update-Database の実行

パッケージマネージャーコンソールから Add-Migration CreateIdentitySchema を実行します (CreateIdentitySchema という名前は任意です)。

xxxxx_CreateIdentitySchema.cs

Migrations と言う名前のフォルダとその中に xxxxx_CreateIdentitySchema.cs というファイル (xxxxx は作成時のタイムスタンプ) が生成されているはずですので確認してください。内容は Pomelo.EntityFrameworkCore.MySql を使った場合と若干異なります。

昔使った MySql.Data.EntityFrameworkCore(今は非推奨)で主キーの長さが指定されてなくて問題となったところは、Pomelo.EntityFrameworkCore.MySql と場合と同様に、varchar(255) に指定されています。

その後、Update-Database を実行すれば接続文字列で指定した名前で MySQL データベースが生成され、その中に ASP.NET Core Identity に必要なテーブルが一式生成されます。

MySQL データベース

Visual Studio から MVC アプリを起動し Register 画面からユーザー登録ができることを確認してください。登録したユーザーは上の MySQL データーベースに反映され、Login 画面から登録した ID とパスワードでログインできるようになります。

この記事の一番上の画像はログインした後で Manage 画面を開いたものです。

Tags: , , ,

CORE

ASP.NET Core で DateOnly 型を使用

by WebSurfer 2022年7月19日 13:28

先の記事「PostgreSQL で Movie チュートリアル (CORE)」で書きましたように、Npgsql 6.0 の timestamp with time zone 型と .NET の DateTime 型の扱いの不整合のため例外がスローされるという問題があります。

その記事では臨時処置的に Npgsql を 6.0 より前の動作に戻すオプションを追加して解決しましたが、恒久処置的には .NET の DateTime 型を DateOnly 型に変えて、対応する PostgreSQL の型を date 型にするべきではないかと思いました。

というわけでそれを試してみましたので以下に顛末を書いておきます。ASP.NET Core はまだ DateOnly 型を十分にサポートしてないようで、すんなりとはいかなかったです。ASP.NET Core で DateOnly 型を使うのは時期尚早なのかも。

(1) DateTime 型を DateOnly 型に変更

Movie クラスの ReleaseDateプロパティの DateTime 型を DateOnly 型に変更します。

DateTime 型を DateOnly 型に変更

(2) Add-Migration の実行

ソリューションをリビルドしてから、Visual Studio のパッケージマネージャーコンソールで Add-Migration DateOnly コマンドを実行します。結果、以下の画像の通り DateOnly クラスが Migrations フォルダに自動生成されます。

Add-Migration の実行結果

Movie クラスの ReleaseDate プロパティの DateOnly 型に該当する PostgreSQL の型は date となっています。

なお、Add-Migration DateOnly の DateOnly という名前は任意に指定できます。指定した名前で xxxxx_DateOnly.cs (xxxxx は作成日時) という名前のファイルが作成され、それに DateOnly という名前のクラスが定義されます。

(3) Update-Database の実行

Visual Studio のパッケージマネージャーコンソールで Update-Database コマンドを実行します。

Update-Database の実行結果

上の画像の ReleaseData 列を見てください。Update-Database の実行前は timestamp with time zone 型であったものが date 型に変更されています。

(4) プロジェクトの実行

以上で完了のはずなのですが、プロジェクトを実行して Index 画面を表示すると ReleaseDate の表示が変でした。html ソースは以下のとおりで、DataOnly のプロパティが表示されます。

<div class="display-label">Year</div>
<div class="display-field">2022</div>
<div class="display-label">Month</div>
<div class="display-field">7</div>
<div class="display-label">Day</div>
<div class="display-field">10</div>
<div class="display-label">DayOfWeek</div>
<div class="display-field">Sunday</div>
<div class="display-label">DayOfYear</div>
<div class="display-field">191</div>
<div class="display-label">DayNumber</div>
<div class="display-field">738345</div>

Movie クラスの ReleaseDate プロパティに DisplayFormat 属性を付与してみましたが何も効果はなかったです。

さらに、Create, Edit 操作では、ブラウザからは期待通りに ReleaseDate=2022-07-18 という形で送信されるものの、アクションメソッドの引数には 0001/01/01 が渡される、すなわちモデルバインディングできてないという問題がありました。

ググって調べてみると、DateOnlyTimeOnly.AspNet という NuGet パッケージが見つかりました。Web API 用とのことですが、とりあえず試してみることにしました。

DateOnlyTimeOnly.AspNet

NuGet パッケージをインストールしたら、Program.cs で UseDateOnlyTimeOnlyStringConverters を AddControllers のオプションとして登録します。

DateOnlyTimeOnly.AspNet

結果、Index 画面の ReleaseDate は 2022/07/18 という形で表示されるようになりました。Create, Edit 操作でのモデルバインディングも正しく行われるようになりました。

もちろん、AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); は削除しても例外は出ず、Create, Edit とも成功します。

ホントはこのような追加のパッケージなしでも DateTime 型を使った場合と同様に動くべきだと思うのですが・・・

Tags: , , , ,

CORE

About this blog

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

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar