前提条件
- .NET SDK 10。本ガイドは .NET SDK 10.0.401、EF Core 10.0.12、
Npgsql.EntityFrameworkCore.PostgreSQL10.0.3 (Npgsql 10.0.3) で動作確認しています。 - ClickHouse Cloud アカウント
- データベース作成用の
psql。CREATE DATABASEステートメントは SQL コンソール で実行することもできます。
ClickHouse Managed Postgres サービスを作成する
ClickHouse Cloud コンソールで New service をクリックし、Postgres を選択します。インスタンスは数分で利用可能になります。詳しい手順については、クイックスタートを参照してください。接続情報を取得する
サービスの左サイドバーで Connect をクリックします。Directly を選択したまま Use SSL をオンにし、パスワードとサーバーのホスト名をコピーします。続いて Download CA certificate をクリックします。証明書は後から Settings → CA Certificate でダウンロードすることもできます。この証明書はインスタンスごとに固有のものなので、Npgsql はこれを使って接続先が正しいサーバーであることを検証できます。 このガイドでは、アプリと EF Core の移行のどちらでも、ポート5432 で Postgres に直接接続します。ASP.NET Core アプリは長時間稼働するプロセスであり、Npgsql がアプリ内ですでに接続をプールしている (デフォルトでは接続文字列ごとに最大 100) ため、別途コネクションプーラーを用意する必要はありません。アプリのインスタンスを多数実行する場合は、PgBouncer を使用するを参照してください。
Connect モーダルには .NET タブがなく、また Npgsql は url タブに表示される postgresql:// 形式の URL を受け付けません。Npgsql では代わりに Keyword=Value 形式の接続文字列を使用します。URL の各値を次のように Npgsql のキーワードに対応付けてください。
プロジェクトのセットアップ
ASP.NET Core プロジェクトを作成し、Npgsql EF Core プロバイダーと EF Core のデザイン時パッケージを追加したうえで、dotnet ef コマンドラインツールをインストールします:
接続を設定する
ダウンロードした CA 証明書をプロジェクトのディレクトリに移動し、ca-certificate.pem にリネームします。
アプリ用のデータベースを作成します。<PASSWORD> とホストは、Connect モーダルに表示される値に置き換えてください。
appsettings.json やソース管理に含まれないよう、接続文字列は Secret Manager に保存します。
Development 環境でユーザーシークレットを読み込みます。dotnet run と dotnet ef はデフォルトでこの環境を使用します。本番環境では、代わりに環境変数 ConnectionStrings__TodoDb を設定してください。
SSL Mode=VerifyFull を指定すると、Npgsql はサーバー証明書が Root Certificate の CA によって署名されていること、および証明書がホスト名と一致することを検証します。Root Certificate に相対パスを指定した場合、Npgsql はカレントワーキングディレクトリを基準に解決します。dotnet run や dotnet ef を使用する場合、これはプロジェクトディレクトリです。別の場所からアプリを起動する場合は、絶対パスを使用してください。
CA が誤っているか存在しない場合、またはホストが証明書と一致しない場合 (IP アドレスで接続した場合など)、接続は
Exception while performing SSL handshake で失敗し、内部エラーとして The remote certificate was rejected by the provided RemoteCertificateValidationCallback が返されます。Root Certificate のファイルが存在しない場合、内部エラーは Could not find file になります。また、次の点にも注意してください。- Npgsql のデフォルトは
SSL Mode=Preferです。また、SSL Mode=Requireも、Root Certificateを設定していても証明書を検証しません。必ずSSL Mode=VerifyFullを設定してください。 - Npgsql は環境変数
PGSSLROOTCERTとPGPASSWORDを読み込みますが、PGHOSTとPGSSLMODEは読み込みません。そのため、Connect モーダルの env タブに表示される変数だけでは不十分です。
モデルを定義する
エンティティクラスを記述したTodo.cs を作成します。
Todo.cs
DbContext を定義した TodoDb.cs を作成します:
TodoDb.cs
Program.cs の内容を置き換えます。このコードでは、TodoDb を Npgsql プロバイダーで登録し、操作ごとに 1 つのエンドポイントをマッピングしています。既存のアプリの場合は、UseNpgsql を指定した AddDbContext の呼び出しを追加するだけで済みます。
Program.cs
/db/tls エンドポイントはアプリ自身のデータベースsessionの pg_stat_ssl を読み取るため、接続が暗号化されているかどうかを確認できます。
スキーマを作成する
モデルから移行を生成し、適用します:dotnet ef migrations add は移行を Migrations フォルダーに書き出し、dotnet ef database update はその移行を適用して __EFMigrationsHistory テーブルに記録します。初回実行時には、EF Core がテーブルを作成する前に __EFMigrationsHistory に対する SELECT の失敗をログに出力しますが、無視して問題ありません。
EF Core は移行を適用する前にロックを取得し、2 つのデプロイメントが同じデータベースを同時に移行しないようにします。Npgsql の場合、このロックは session レベルのアドバイザリロックではなく、移行トランザクション内で __EFMigrationsHistory に対して実行される LOCK TABLE です。そのため、ロックはトランザクションの終了時に解放されます。
実行と動作確認
ポート3115 でアプリを起動します。
DELETE リクエストは 204 No Content を返すため、その行は空になります。
アプリのデータベース session で TLS が使用されていることを確認します。
guide_dotnet、続いて public を展開して、Todos テーブルをクリックします。EF Core はクラス名とプロパティ名を引用符付きの識別子として使用するため、テーブル名は "Todos"、カラム名は "Id"、"Title" などになります。テーブルには、残った 1 行が格納されています。
PgBouncer を使用する
Npgsql は、アプリのインスタンスごとに最大 100 個の接続を開きます (Maximum Pool Size)。複数のインスタンスの接続数の合計が Postgres の接続上限に近づく場合は、同梱の PgBouncer を経由してアプリを接続してください。Connect モーダルで via PgBouncer を選択し、接続文字列のポートを 6432 に変更します。
- プリペアドステートメント: EF Core はステートメントを準備せず、Npgsql の自動準備もデフォルトで無効 (
Max Auto Prepare=0) になっています。そのため、クエリは名前なしステートメントを使用し、PgBouncer 経由でも問題なく動作します。この設定は変更しないでください。PgBouncer 経由では、Max Auto PrepareやNpgsqlCommand.Prepareによるプリペアドステートメントが08P01: prepared statement did not existで断続的に失敗します (No Reset On Close=trueを指定すると防げます)。また、移行によってカラムの型が変更されると、その後は新しい接続であってもcached plan must not change result typeで失敗し続けます。プリペアドステートメントが必要な場合は、直接接続してください。 - セッション状態:
search_pathを含め、SETで行った設定はトランザクションをまたいで引き継がれません。代わりに、トランザクション内でSET LOCALを使用してください。PgBouncer は接続文字列のキーワードSearch PathとOptionsをunsupported startup parameterで拒否しますが、TimezoneとApplication Nameは使用できます。 - 移行: 移行のロックはトランザクションの終了とともに解放されるテーブルロックであるため、
dotnet ef database updateも PgBouncer 経由で実行できます。 - TLS の確認:
pg_stat_sslが示すのは PgBouncer と Postgres 間の接続であるため、/db/tlsはnoneを返します。ただし、アプリケーションから PgBouncer への接続は引き続き検証されており、誤った CA 証明書を使用すると同様に失敗します。