ASP.NET でSupabaseのPostgreSQLを使う

はじめに

PostgreSQLの環境をローカルではDockerを使っていたが、SupabaseのDBを使ってみたいなと思ったので使ってみる。

環境

Windows 11 Professional
Visual Studio 2022 Community

準備

でアカウントを作成していること。

プロジェクトを作成する

create-project-01

手順

接続文字列の取得

サイドバーの「Project Settings」を選択する

get-connection-string-01

「Database」を選択する

get-connection-string-02

Connection Parametersを確認して接続文字列を作成する

get-connection-string-03
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
-    "PostgreSqlConnection": "Host=localhost;Port=5432;Database=postgres;Username=postgres;Password=postgres",
+    "PostgreSqlConnection": "Host=[Host];Port=6543;Username=[UserName];Password=[Password]"
  }
}

DBのマイグレートを実行する

※今回使用しているプロジェクトはDBのマイグレートをできるようにしているので、以下でマイグレートをする。

  1. メニューバーの「ツール」 > 「NuGet パッケージ マネージャー」 > 「パッケージ マネージャー コンソール」を選択
  2. NuGetコンソールで以下を実行する。
Update-Database

エラーになる↓

ログ
PM> Update-Database
Build started...
Build succeeded.
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (28ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT EXISTS (
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
      Failed executing DbCommand (32,060ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT EXISTS (
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
Failed executing DbCommand (32,060ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT EXISTS (
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
Npgsql.NpgsqlException (0x80004005): Exception while reading from stream
 ---> System.TimeoutException: Timeout during reading attempt
   at Npgsql.Internal.NpgsqlReadBuffer.<Ensure>g__EnsureLong|55_0(NpgsqlReadBuffer buffer, Int32 count, Boolean async, Boolean readingNotifications)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
   at Npgsql.Internal.NpgsqlConnector.ReadMessageLong(Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
   at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
   at Npgsql.NpgsqlDataReader.NextResult()
   at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteScalar(Boolean async, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteScalar()
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteScalar(RelationalCommandParameterObject parameterObject)
   at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.Exists()
   at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Internal.NpgsqlMigrator.Migrate(String targetMigration)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String connectionString, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String connectionString, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Exception while reading from stream
PM> 

調べたところ、以下のStackOverflowがでてきた。

https://stackoverflow.com/questions/78792760/cant-use-entity-framework-core-with-supabase-postgressql

コメントに、6543ではなく、5432のセッションモードを利用すると解決したとのことでやってみる。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
-    "PostgreSqlConnection": "Host=[Host];Port=6543;Username=[UserName];Password=[Password]"
+    "PostgreSqlConnection": "Host=[Host];Port=5432;Username=[UserName];Password=[Password]"
  }
}
ログ
PM> Update-Database
Build started...
Build succeeded.
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (29ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT EXISTS (
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (12ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT EXISTS (
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (25ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "__EFMigrationsHistory" (
          "MigrationId" character varying(150) NOT NULL,
          "ProductVersion" character varying(32) NOT NULL,
          CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId")
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (13ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT EXISTS (
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
    SELECT 1 FROM pg_catalog.pg_class c
    JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace
    WHERE n.nspname='public' AND
          c.relname='__EFMigrationsHistory'
)
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (9ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT "MigrationId", "ProductVersion"
      FROM "__EFMigrationsHistory"
      ORDER BY "MigrationId";
Microsoft.EntityFrameworkCore.Migrations[20402]
      Applying migration '20241110103316_InitialCreate'.
Applying migration '20241110103316_InitialCreate'.
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (20ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE todos (
          id integer GENERATED BY DEFAULT AS IDENTITY,
          title character varying(100) NOT NULL,
          description text NOT NULL,
          due_date date NOT NULL,
          is_completed boolean NOT NULL,
          CONSTRAINT "PK_todos" PRIMARY KEY (id)
      );
Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (12ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
      VALUES ('20241110103316_InitialCreate', '8.0.10');
Done.
PM> 

問題なく実行できた!

Supabaseでテーブルの確認

Databaseを選択する

confirm-table-01

確認

onfirm-table-02

todosテーブルが作成されていることを確認。

画面からの確認

todo-app-01

CRUD操作が問題ないことを確認できたのでOK

おまけ

C#ではSupabaseのライブラリを使ってDB操作ができるようだ。
その手順は下記で実施する。

https://supabase.com/docs/reference/csharp/initializing

Supabaseを入れる

https://github.com/supabase-community/supabase-csharpsupabase-csharpは非推奨らしい。

  1. プロジェクトを右クリック、「NuGetパッケージの管理」を選択する
  2. Supabase」をインストールする
  3. インストール完了後、依存関係に 「Supabase」が入っていることを確認
install-supabase-01

SupabaseクライアントでのCRUD処理は書いていないのここまで。

参考

おわりに

SupabasePostgreSQLへのDB接続を行った。
今後はこっちの環境をベースに作業をしようと思う。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。