C# - TransactionScope(トランザクションスコープ)の使い方

C# の トランザクションとは

ここで言う 「 トランザクション 」 とは、一連の処理単位のことです。

トランザクション内の処理は、全てが成功した時のみ確定され、処理の途中でエラーが起きた場合は、そこまでの処理をロールバックして、トランザクション処理実行前の状態まで戻ります。

データベース側でトランザクションに入れられる時は良いですが、複数のデータベースを更新する必要があったり、途中でクライアント側処理を挟む必要があったりと、データベース側ではトランザクションに入れられない時もあると思います。


C# では、TransactionScope クラスを使って、簡単にデータベースに関連する処理をコード側からトランザクションとして処理することができます。

エラーが出るとロールバックしてくれるので、データの整合性を保つのにとても便利です。


System.Transactions を [参照] に追加する

まず、TransactionScope を使いたいので、参照に System.Transactions を追加します。

ソリューションエクスプローラーで [参照] を右クリックし [参照の追加] をクリックします。

C# - トランザクションスコープの使い方 1


[アセンブリ][フレームワーク] から System.Transactions を選択し、[OK] をクリックします。

C# - トランザクションスコープの使い方 2


[参照]System.Transactions が追加されました。

C# - トランザクションスコープの使い方 3


以下の using ディレクティブを追加しておいてください。

using System.Transactions;

C# - トランザクションスコープの使い方 4

TransactionScope を使ってトランザクションを実行する

違うサーバにあるデータベースとの分散トランザクションも可能ですが、設定がいろいろと必要になりますので、今回は同じ PC にインストールされた別のインスタンスのデータベースに接続して試します。

同じ PC 上にある MSSQLSERVER2017 という名前付きインスタンスと、SQLEXPRESS という名前付きインスタンス上の Test データベースに接続します。

App.config ファイルはこんな感じです。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="DB1" 
         connectionString="Server=.\MSSQLSERVER2017;Database=Test;Uid=sa;Pwd=*******"/>
    <add name="DB2"
         connectionString="Server=.\SQLEXPRESS;Database=Test;Uid=sa;Pwd=*******" />
  </connectionStrings>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
</configuration>

C# - トランザクションスコープの使い方 5


次のコードで、TransactionScope を使って、両方のデータベースに存在する uspTest というストアドプロシージャを実行します。

using (TransactionScope scope = new TransactionScope())
{
    using (SqlConnection conn1 = 
            new SqlConnection(ConfigurationManager.ConnectionStrings["DB1"].ConnectionString))
    {
        conn1.Open();

        SqlCommand cmd1 = new SqlCommand("uspTest", conn1);
        cmd1.CommandType = CommandType.StoredProcedure;
        cmd1.Parameters.AddWithValue("ExecutedFrom", "Test1");
        cmd1.ExecuteNonQuery();

        using (SqlConnection conn2 = 
                new SqlConnection(ConfigurationManager.ConnectionStrings["DB2"].ConnectionString))
        {
            conn2.Open();

            SqlCommand cmd2 = new SqlCommand("uspTest", conn2);
            cmd2.CommandType = CommandType.StoredProcedure;
            cmd2.Parameters.AddWithValue("ExecutedFrom", "Test2");
            cmd2.ExecuteNonQuery();
        }
    }

    scope.Complete();
}

C# - トランザクションスコープの使い方 6

scope.Complete(); まで到達すれば、変更が反映されますが、エラーや return などで、そこに到達する前に TransactionScope のスコープを抜けると、データベースへの変更は自動的にロールバックされます。


uspTest の内容はこんな感じで、実行されると TestResult テーブルに @ExecutedFrom と実行時間がインサートされるだけです。

C# - トランザクションスコープの使い方 7

C# - トランザクションスコープの使い方 8


このコードを実行すると、次のように両方のデータベースの uspTest が実行され、TestResult テーブルにレコードがインサートされました。

C# - トランザクションスコープの使い方 9


TransactionScope の Isolation レベル(トランザクション分離レベル)は指定可能ですが、 デフォルトは Serializable (直列化可能)になっています。

Serializable (直列化可能)は一番強く、安全な分離レベルです。 このトランザクションが読んだデータは、このトランザクションが終わるまで他のトランザクションが変更することができません。 また、他のトランザクションがこのトランザクションに影響するような新しいデータを挿入することもできません。

その分、一番他のトランザクションをブロックしますので、この Isolation レベルで長い処理をトランザクションとして実行しないように気をつけてくださいね!

MSDTC on server [サーバー名] is unavailable のエラーが出た場合

2 つめのデータベースの接続を Open した時に 「 MSDTC on server [サーバー名] is unavailable 」 というエラーが出た場合は、Distributed Transaction Coordinator サービスが停止している可能性があります。

C# - トランザクションスコープの使い方 10


検索で Services.msc と入力して Services を起動してください。

C# - トランザクションスコープの使い方 12


そこで Distributed Transaction Coordinator を探し、停止している場合は右クリックでサービスを開始した後で、再度実行してみてください。

C# - トランザクションスコープの使い方 11


© 2024 C# 入門