今まで独自の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