QA@IT

ASP.NETのカスタムトランザクションスコープの作成方法

5315 PV

ASP.NET MVCの開発で、Npgsqlを使ったトランザクションスコープの作成を考えています。
トランザクションスコープクラス内のコンストラクタでコネクションとトランザクションを作成し、
トランザクションスコープが破棄されるまで、データベースアクセス時にはトランザクションスコープクラスが作成したコネクションとトランザクションを使用するようにしたいです。

ThreadStaticというものがあると知ったのですが、こちらを使い、コネクションとトランザクションを制御すれば大丈夫でしょうか。

クラス

public class TransactionScopeCustom : IDisposable
{
    [ThreadStatic]
    private static NpgsqlConnection _connection;

    [ThreadStatic]
    private static NpgsqlTransaction _transaction;

    private bool isComplete = false;

    public TransactionScopeCustom()
    {
        if (_connection != null || _transaction != null)
        {
            throw new Exception("TransactionScopeCustomのコンストラクタで既にコネクションかトランザクションがある");
        }
        _connection = new NpgsqlConnection(DataBaseAccessHelper.GetConnectionString());
        _connection.Open();
        _transaction = Connection.BeginTransaction();
    }

    public static NpgsqlConnection Connection { get { return _connection; } }

    public static NpgsqlTransaction Transaction { get { return _transaction; } }

    public void Complete()
    {
        this.isComplete = true;
    }

    public void Dispose()
    {
        if (this.isComplete)
        {
            _transaction.Commit();
            _connection.Close();
            _transaction = null;
            _connection = null;
        }
        else
        {
            _transaction.Rollback();
            _connection.Close();
            _transaction = null;
            _connection = null;
        }

    }
}

使い方(スコープ)

        using (TransactionScopeCustom ts = new TransactionScopeCustom())
        {
            Dac dac = new Dac();
            dac.Update();

            ts.Complete();
        }

使い方(コネクション,Update内)

NpgsqlConnection cn = TransactionScopeCustom.Connection;
  • そうすることでどういうメリットがあるのか教えていただけませんか。Npgsql も SqlClient と同様に接続プールを利用しての接続になるのですよね? であれば、普通に、接続が必要になる直前に Open し、終わったら即 Close するという原則に従えばよさそうに思いますが。 -
  • ConnectionでなくTransactionの管理が目的です。
    複数のクラスで行われるデータベースへの処理を同一のトランザクションで行いたいとき、トランザクションとトランザクションを開始したコネクションを、引数で渡す方法しか思いつきませんが、それが不要になります。
    -
  • 回答をありがとうございました。目的は理解しました・・・が、やはり自分的には、せっかく回答してもらったのにすみませんが、そういうことをすることには納得できてないです。簡単に、かつリスクなく渡せる手段がいくらでもあるのだから、変なこと(失礼)はしない方がいいと思ってしまいます。 -
  • 標準であるTransactionScopeの代替みたいなものでしょう。 -
  • ええとつまり、別に昔からある一般的な考え方だって事です。まあ引数をなくすことそのものが目的というわけではありませんが。 -
  • 異なる接続の SQL を一つのトランザクションに束ねるのではなさそうなので、それなら接続やトランザクションを渡して処置することが可能なはずだから、危険な匂いのする変なこと(失礼)はしない方がいいと思ったのですが。
    -
  • 昔からある(?)自動トランザクション(異なる接続の SQL を一つのトランザクションに束ねる)が必要なら、very early stage ながら実装が始まったそうですので、それを評価した方がよいのでは?
    https://github.com/npgsql/npgsql/wiki/User-Manual
    -
  • 自動トランザクションは異なる接続を束ねるだけが目的ではありませんし、今回のは違う方でしょう。 -
  • ああ、トランザクションスコープへの対応が始まってるってことですね。これがあるなら確かに評価はしてみた方がいいかもしれません。ちょっと早すぎるかもしれないのでそこは微妙ですが。 -
  • 早すぎるリスクと独自でやるリスクどっちをとるかですね。 -

回答

とりあえずいくつか注意した方がいい点ですが。

・スレッドなどに保存するデータは専用にクラスを作成して、単一のインスタンスで判断できるようにする方がよい。
 ※中途半端に片方だけ残ったりす余地をなくすためです。

・保存場所はThreadStaticではなく、CallContextかHttpContextが望ましい。
 意味的にはHttpContextがより望ましい。
 ※単純なスレッドローカルだと、予期せぬ例外などで消し損ねた場合がまずいです。
  また、非同期コントローラその他でのスレッド切り替えも考慮するなら、HttpContextの方が良いです。
  もっともそうしたからと言って必ず問題なくうまく行くとは限りませんが。

とりあえず最低限この辺りですかね。
本当は入れ子とかに対応する方が汎用性が高くなって望ましいと思いますけど。
まあ限られた用途に限定してシンプルにしておくのもそれはそれでありかもしれません。

編集 履歴 (0)
  • HttpContextBaseのItemsに値を格納できるようですが、「 private static NpgsqlConnection _connection」に格納するのをやめて、そちらにいれる形でしょうか? -

ThreadStaticというものがあると知ったのですが、こちらを使い、コネクションとトランザクションを制御すれば大丈夫でしょうか。

async/await を使う場合、期待した動作を得られないはずです。

編集 履歴 (1)
ウォッチ

この質問への回答やコメントをメールでお知らせします。