none
entity frameworkを使用したトランザクション分離レベルの制御について RRS feed

  • 質問

  • visual studio 2010 + sqlserver 2008を使用して開発を行っています。

    TransactionScopeを使用しない状態で、トランザクションの制御を行おうと考えていますが、下記2点の方法が分かりません

    ①自動コミットする際のトランザクション分離レベル(isolationlevel)の変更方法

    ②下記のコードで手動トランザクションを開いた際に、ReadUncommittedではなく、Serializableで実行される。

    using(testEntities db = new testEntities()){
                    ( db.Connection as EntityConnection ).Open();
                    EntityTransaction tran = ( db.Connection as EntityConnection ).BeginTransaction( IsolationLevel.ReadUncommitted );
                    IEnumerable<Account> list = from a in db.Account
                                                where a.AccountID == 1
                                                select a;
                    Account account = list.First();
                    account.Name = "テストタロウ";
                    db.SaveChanges();
                    //ここでストップ
                    tran.Commit();
                }

    SaveChanges()を呼んだ後、Commitされる前にAccountID=1の行がロックされていて、

    AccountID=1の行を含んだSelectもかけることが出来ません

    どうかよろしくお願い致します。

    2011年3月2日 5:29

回答

  • ①自動コミットする際のトランザクション分離レベル(isolationlevel)の変更方法


    たしか無理だったと思います。

    ②下記のコードで手動トランザクションを開いた際に、ReadUncommittedではなく、Serializableで実行される。


    EntityConnection.BeginTransaction() で作成したEntityTransactionをEntityCommandに渡していないので何も行われていないように思いますが…。リンク先には「返されたEntityTransactionインスタンスを後でEntityCommandに関連付けることで、そのトランザクション内でコマンドを実行できます。」と書かれています。

    結局TransactionScopeを使用することをお勧めします。

    • 回答としてマーク 山本春海 2011年4月4日 2:19
    2011年3月2日 6:23
  • EF は初期のものから最新のCTPまで色々ありますし、使い方によっても多少の差異はあると思うのですが、コンテキストからトランザクションを分離するには、

    using (var conn = CreateConnection())
    {
      conn.Open();
      using (var tran = BeginTransaction(conn))
      {
        using (var db = new testEntities(conn))
        {
          /* ... */
          db.SaveChanges();
        }
    
        using (var db = new testEntities(conn))
        {
          /* ... */
          db.SaveChanges();
        }
    
        tran.Commit();
      }
    }
    
    みたいなかんじで任意のトランザクションに参加することが可能です。※ TransactionScope がおすすめですけどね
    • 回答としてマーク 山本春海 2011年4月4日 2:19
    2011年3月2日 23:42

すべての返信

  • ①自動コミットする際のトランザクション分離レベル(isolationlevel)の変更方法


    たしか無理だったと思います。

    ②下記のコードで手動トランザクションを開いた際に、ReadUncommittedではなく、Serializableで実行される。


    EntityConnection.BeginTransaction() で作成したEntityTransactionをEntityCommandに渡していないので何も行われていないように思いますが…。リンク先には「返されたEntityTransactionインスタンスを後でEntityCommandに関連付けることで、そのトランザクション内でコマンドを実行できます。」と書かれています。

    結局TransactionScopeを使用することをお勧めします。

    • 回答としてマーク 山本春海 2011年4月4日 2:19
    2011年3月2日 6:23
  • 早い回答ありがとうございます。

    ①自動コミットのトランザクション分離レベルの変更は無理ですか、残念です。

    ②についてですが、上記のコードのままでも、トランザクション処理そのものは正常に動作しています。

    例えば上記のコード上で、最後のtran.Commit();の部分をtran.Rollback();にすると正常にRollbackされます。

    複数のテーブルへのInsertやUpdate等も正常に一括でCommit,Rollbackが可能になります。

    Isolationlevelの変更のみが動作していない状態です。

    ただ、提示いただいたリンク先を読む限りだと、佐祐理さんのおっしゃるように、EntityCommandに関連付けないと何も動作しないように見えます。

    もう少し調べてみて無理なようでしたらTransactinScopeを検討してみます。

    ありがとうございました。

    2011年3月2日 8:41
  • ふと疑問に思ったのですが、指定したReadUncommittedではなくSerializableで動作していると判断した根拠はなんでしょうか?

    ReadUncommittedは他のトランザクションの未コミットデータが参照できるだけで、逆にReadUncommittedで更新した内容が他のトランザクションから参照できるようになるわけではありません。

    2011年3月2日 12:15
  • EF は初期のものから最新のCTPまで色々ありますし、使い方によっても多少の差異はあると思うのですが、コンテキストからトランザクションを分離するには、

    using (var conn = CreateConnection())
    {
      conn.Open();
      using (var tran = BeginTransaction(conn))
      {
        using (var db = new testEntities(conn))
        {
          /* ... */
          db.SaveChanges();
        }
    
        using (var db = new testEntities(conn))
        {
          /* ... */
          db.SaveChanges();
        }
    
        tran.Commit();
      }
    }
    
    みたいなかんじで任意のトランザクションに参加することが可能です。※ TransactionScope がおすすめですけどね
    • 回答としてマーク 山本春海 2011年4月4日 2:19
    2011年3月2日 23:42
  • //ここでストップ

    ステップ実行している際に上記の場所で止めて

    ManagementStudioからSelectをかけると、反応が応答なしになります。

    応答なしの状態でステップ実行をCommitが終わる所まで進めると即座にSelectの結果が返ってきます。

    上記結果を元にSerializableで動作していると判断しました。

    また、他にもわかったことがありますが、それは追記します。

     

    2011年3月3日 3:25
  • だいたい分かりました。

    結論としては、上記のIsolationLevelの変更は効いていました。

    ただし、Insert,Update,Deleteに関してはSerializableで動作いたします。

    確認方法:

    ①account.Name = "テストタロウ";の行までステップ実行を行う

    ②ManagementStudioから、AccountID=1に対してUpdateを行う

    上記条件でIsolationLevelをReadUncommittedと Serializableの両方で試したところ

    ReadUncommitted:Updateが正常に行われる。

    Serializable:行がロックされている為、応答なしになる。

    *tran.Commit()が走った時点で即座に反映されます。(Updateがかかる。)

    次に

    ①ステップ実行//ここでストップ の所まで進める。

    ②ManagementStudioから、AccountID=1に対してSelectをかける。

    上記条件では、ReadUncommitted,Serializableの両方とも、応答なしになります。

    *tran.Commit()が走った時点で即座にSelectの結果が表示されます。

    2011年3月3日 3:44
  • 回答ありがとうございます。

    TransactionScopeを使用することにしました。

     

    2011年3月3日 3:46
  • データの読み取りがロックされることと、トランザクションの分離レベルが Serializable であることは関係ないのでは?

    1. トランザクション a で select を実行 → 結果 A
    2. 別のトランザクション b で A を B に変更
    3. トランザクション a で再び select を実行 → 結果 X

    という順序の中で、

    • ReadCommitted なら、トランザクション b が COMMIT されていれば X=B 、COMMIT 前であれば X=A
    • ReadUncommitted なら、常に X=B
    • Serializable なら、先に始まった a は、b の影響を受けないので a から見たら X=A
    • Snapshot なら常に X=A

    みたいな話じゃないですか?

    確認されるべきは、.First() を呼び出す前に停止しておいて、Management Studio から UPDATE を発行してコミットしていない状態で、Management Studio で変更した内容が受け取れるかどうかではないでしょうか?

    > ReadUncommitted:Updateが正常に行われる。

    Management Studio を Read Commited で動かしているのならば、まだコミットされていない変更は見えないので UPDATE を実行できます。その後、Management Studio から実行した UPDATE の内容が、楽観的ロックの更新チェック対象外の場合は、Management Studio から実行した UPDATE の内容が無視されてプログラム側の値で上書きされるので表面的には正常に行われているように見えるかもしれません。(それが期待する動作なら、問題ないとは思います) 逆に、UPDATE で更新されたフィールドが更新チェック対象フィールドである場合には SaveChanges() から楽観的ロックによる保護例外が飛んで異常終了するかと思います。

    > Serializable:行がロックされている為、応答なしになる。

    後から開始された Visual Studio 側のトランザクションは、前に開始している Serializable のトランザクションが確定するまで UPDATE の対象レコードを確認できないため、期待通りの正常な動作ですよね?

    > ①ステップ実行//ここでストップ の所まで進める。
    >
    上記条件では、ReadUncommitted,Serializableの両方とも、応答なしになります。

    Management Studio を Read Commited で動かしているのならば、プログラムの UPDATE によるロックを待機するので、期待通りの正常な動作ですよね。

    上記の結果から、

    > ただし、Insert,Update,Deleteに関してはSerializableで動作いたします。

    なんてことはない、と言えると思いますが。

    2011年3月3日 5:14
  • こんにちは、ユニーク さん。

    MSDN フォーラムのご利用ありがとうございます。オペレーターの山本です。

    皆さんからの情報が参考になったようでしたので、勝手ながら私のほうで回答としてマークさせていただきました。

    いただいた情報の中で、解決に役立った投稿や、参考になる情報などの有効な情報には回答としてマークすることをお願いしています。
    今後、同じ問題でこのスレッドを参照される方にも、有効な情報がわかりやすくなるかと思いますので、ご協力よろしくお願いいたしますね。

    よろしくお願いいたします。それでは。
                                                                                                                                                                           
    日本マイクロソフト株式会社 フォーラム オペレーター 山本 春海

    2011年4月4日 2:24