なべひろBlog

プログラミングをメインに仕事に関するアレコレを発信しています。

C#でNpgsqlを使ってPostgreSQLへアクセス【Transaction】

トランザクションはあまり多くの説明は必要としないかと思われますのでサクッとやってしまいます。
どちらかと言えばロールバックが発生した後の処理をどうするかが悩ましい所かもしれません。
私の作成するサンプルソースファイルは
今回のサンプルソースは
  1. データベーステーブルを作成する
  2. データを2つINSERTする
として、この2つの処理後にコミットしてみます。
using NpgsqlConnection con = new("Server=127.0.0.1; Port=5432; User Id=test_user; Password=pass; Database=db_PostgreTest; SearchPath=public");
con.Open();
// con.Open()前に行うと例外「InvalidOperationException」になる。
using NpgsqlTransaction tran = con.BeginTransaction();
try
{
    using NpgsqlCommand cmd = new("CREATE TABLE data(id serial PRIMARY KEY, time timestamp DEFAULT clock_timestamp(), name text, numeric integer)", con);
    _ = cmd.ExecuteNonQuery();
    // データを2つINSERTする
    cmd.CommandText = "INSERT INTO data(name, numeric) VALUES ('a', 1);";
    int result1 = cmd.ExecuteNonQuery();
    cmd.CommandText = "INSERT INTO data(name, numeric) VALUES ('b', 2);";
    int result2 = cmd.ExecuteNonQuery();
    tran.Commit();
    // Commitメソッドが実行(成功)した後にRollbackメソッドを実行しても例外「InvalidOperationException」になる。
    //tran.Rollback();
}
catch (PostgresException)
{
    tran.Rollback();
}
上記4行目がトランザクションの生成ですが、注意する点としてはNpgsqlConnectionのOpenメソッド実行後になる事です。
Openメソッド実行前にトランザクションを生成すると例外「InvalidOperationException」が発生します。
実際、データベーステーブルがどのタイミングで生成されているかプログラムをステップ実行して確認してみます。
上記サンプルプログラムをステップ実行で13行目までを実行しpgAdmin4で確認した結果は
トランザクション前
「テーブル」文字の左に「>」がなく、配下には何も存在していない事が確認できます。
つまりそれまでに記述したCREATE TABLEやINSERTがまだ実行されてないのが分かります。
この状態14行目のCommitメソッドを実行しpgAdmin4のテーブルにマウスカーソルを合わせ「右クリック」-「再読み込み」を行い「テーブル」のツリーを展開するとテーブルが存在している事が確認できます。
トランザクション後
もし例外が発生して正しくCREATE TABLEやINSERTが実行されなかった場合はRollbackメソッドを実行し、その後はイレギュラー時に必要な処理を追記してください。
ちなみにトランザクションのCommitメソッドが正常に終了したあとに「やっぱりやめた」のRollbackメソッドは例外「InvalidOperationException」が発生して正しく処理されません。
あまり使わないかもしれませんが、こんな手法もあります。
using NpgsqlConnection con = new("Server=127.0.0.1; Port=5432; User Id=test_user; Password=pass; Database=db_PostgreTest; SearchPath=public");
con.Open();
// con.Open()前に行うと例外「InvalidOperationException」になる。
using NpgsqlTransaction tran = con.BeginTransaction();
try
{
    using NpgsqlCommand cmd = new("CREATE TABLE data(id serial PRIMARY KEY, time timestamp DEFAULT clock_timestamp(), name text, numeric integer)", con);
    cmd.Transaction = tran;
    _ = cmd.ExecuteNonQuery();
    // データをINSERTする
    cmd.CommandText = "INSERT INTO data(name, numeric) VALUES ('a', 1);";
    int result1 = cmd.ExecuteNonQuery();
    cmd.CommandText = "INSERT INTO data(name, numeric) VALUES ('b', 2);";
    int result2 = cmd.ExecuteNonQuery();
    cmd.Transaction.Commit();
}
catch (PostgresException)
{
    tran.Rollback();
}
NpgsqlCommandのTransactionプロパティに入れておき、そのTransactionプロパティのCommitメソッドを実行すると同じ動作になります。
関連記事