2012年12月6日

ASP.NET MVCでDbContextのトランザクションと独自SQL 処理のトランザクションを同一のスコープにする

最近毎日 ASP.NET MVC4 でWebアプリケーションを作っている。

今まで独自のO/Rマッパを使っていたので Entity Framework を使い出したのは実はごく最近。

これでSQLを書かなくてもデータの取得や更新が簡単に出来る様になったのは嬉しいのだけれど、当然ストアドプロシージャを呼んだり自分で書いたSQL文を実行したりしたいという場面も結構ある。

そこでいくつかのDBアクセスのパターンを試していて、独自SQLの実行とDbContext経由でのデータ更新を同一のトランザクション内で行いたい、という場合にちょっと困ったので対処方法をメモしておきたい。



とりあえず問題ない例

上の例では別のContextを使っているのでこれはエラーにはならない。 ただしこれだと1つめの更新処理と2つめの更新処理を同一のトランザクション内で実行出来ない。



問題になる例

上の例の様にDbContextからの更新処理をADO.NETのTransaction内に入れた場合は次のようなエラーが起きる。





対処の方法


1. 生成済みのDbConnectionを渡してDbContextを生成する。

DbContextのインスタンスを生成する時に、コンストラクタに既存のDbConnectionを渡すとDbContextで勝手にCloseされる事が無くなる。

これをするためには、DbContextのクラスにDbConnectionパラメータを受け取るコンストラクタを追加する必要がある。



    public class MyEntities : DbContext {

        public MyEntities()
            : base("DefaultConnection") {
        }

        public MyEntities(DbConnection existingConnection)  
            : base (existingConnection, false){ 
        }

        public DbSet<Product> Products { get; set; }
    }


2. EntityConnectionを使ってコネクションをOpenする。

DbContextのDatabase.Connectionは素のSqlConnectionを返す。ただしこれを自分で直接OpenしてしまうとEFの管理情報と一致しなくなる。

DbContextをIObjectContextAdapter型にキャストしてそこからObjectContextを取得し、そのConnectionプロパティ(実態はEntityConnection)に対してOpenを実行すればEFの管理内で処理される。


3. EntityConnectionに対してBeginTransationする。

1、2の後、EntityConnectionに対してBeginTransationしてトランザクションを開始すれば、独自のSQL処理とDbContextのSaveChangesメソッドによる内部的なトランザクションとが同一のスコープで実行可能になる。


対処後の例



動作確認可能な全ソースを含んだサンプルプロジェクトはこちら


参考にしたURL

This SqlTransaction has completed; it is no longer usable. Entity Framework Code First - Stack Overflow

task parallel library - Entity Framework, SqlCommand and TransactionScope - Stack Overflow

Exception from DbContext API: EntityConnection can only be constructed with a closed DbConnection - Diego Vega - Site Home - MSDN Blogs

EntityConnection can only be constructed with a closed DbConnection

2011 7月 « Do Design Space

.net - Frequent saves with entity framework - Stack Overflow

entity framework - EF Code First DBContext and Transactions - Stack Overflow