読者です 読者をやめる 読者になる 読者になる

餃子が食べたい

メガネが最も必要な時、メガネは必ず手元にないのである

EntityFrameworkでsavechanges()をしたときにEntityExceptionが出る場合の対策(Foreach文に注意)

C#

SEなんでたまにはそれっぽいもの書こうかな…

EntityFrameworkとは

.Netでサポートされているフレームワークで、もともとはコードファーストでデータベースを作って、それを簡単に操作するような目的で作られたもの

詳しくは、ググれば使い方とかも簡単にわかるし、今回の本題はそこではない

EntityFrameworkのSaveChanges()はちょくちょく例外ぶん投げてくる

EntityFrameworkの中で、加えた変更はSaveChanges()を実行するまではほとんどの場合で反映されない

(例えば、直接SQL文を実行する使い方もあって、それをやると即時反映されるけど)

で、SaveChanges()の時には整合性が求められるので、ちょくちょく例外が投げられる

例えば

・マルチスレッドで、別々にデータベースを呼び出し、変更を加える

→自分以外のスレッドが処理中の場合、EntityExceptionが出る

 

マルチスレッドでEntityFramework使うならロックかけて、順次実行されるようにする工夫は必要

 

マルチスレッドでもないのに、SaveChanges()時にエラーが出る

例えば、hogehogeEntitiesで呼び出す

honyararaとnantokaっていうテーブルがあるデータベースに変更を加えるとする

こんなコードがあったとする

using(var context = new hogehogeEntities(){

   foreach(var item in context.honyarara){

     context.nantoka(x=>x.id = item.id).value = 1;

     context.SaveChanges();

  }

}

内容としては、honyararaとidが一致するnantokaのvalueを1に書き換える架空のプログラム

ただ、これは100%例外を吐き出す

honyararaには変更を加えていないので、foreach文が回らなくなるわけではない

 

恐らく、理由はforeach文でcontextを呼び出しているのと並行して内部でcontextを呼び出しているので競合して、EntityExceptionが出るのだろう

 

どうすればよいかと言うと、

 

using(var context = new hogehogeEntities(){

   foreach(var item in context.honyarara.ToList()){

     context.nantoka(x=>x.id = item.id).value = 1;

     context.SaveChanges();

  }

}

で回せば、問題は起きなくなる

contextそのものでなく、List化した項目に対してforeach文を回しているから、問題ない

 

今回の例みたいな単純なプログラムだと、すぐ原因は分かるけれども、メソッド呼び出してその中で変更を加えて、、みたいなことをすると原因がわからなくなるので、EntityFrameworkを使うときはForeach文には気を付けて下さい