しょっちゅう更新しようと考えていたブログでしたが、途中からそんな余裕もなく、
3ヶ月休みなしで出勤しつつ、極めつけは客先(宿泊施設)に3日間滞在しつつようやく終えることができました。
さて、ほとんどの問題は解決することができたものの、いまだ2つだけ心残りがあります。
ひとつは、MVC的な方法を適用したとき、バリデーションがモデル内で完結できない点です。
NOT NULLな属性の入力値がNULLであることの検証は、コントローラにしかできません。
まあ、全部Nullableにすればいいのかもしれませんが。
もうひとつは、トランザクションがフレームワークまかせになっている点です。
LINQ to Entities + SQLiteでの話なので、他のサーバクライアント型のDBを使った場合は問題ないのかもしれません。
SQLiteの場合、IsolationLevelはSerializableかReadCommittedが使えるらしいですが、デフォルトはSerializableになっています。
マルチスレッドアプリケーションで、バックグラウンドで時間のかかる帳票作成などしていると、場合によっては並行して書き込み処理が発生することがあります。
というか、今回のアプリではいろんなところからパケットを受信してはDBに保存するので、読み込みと書き込みがバッティングするのは普通に起こっていました。
その場合に読み込み処理の方が、Serializableでトランザクションを実行していると、SaveChanges()の方がタイムアウト待ちになって、しかも読み込み処理の方も影響を受けてどちらも無意味に遅くなります。
かといって、ReadCommittedを使ってみると、なぜかSaveChanges()メソッドに失敗して変更が保存されません。
結局、並行処理はあきらめて、読み込み処理中には保存処理は全て拒否して、
別途ファイルに保存しておいたイベントデータを暇なときに読み込むという方法をとるしかありませんでした。
それから、これは解決することができたんですが、メソッドチェーンでWhereの条件を追加しながら結果を絞り込む方法を使うときは、クエリの遅延実行が働くようになっているようなんですが、これを下手に使うとループや属性へのアクセスごとに接続が実行されて、非常に遅くなります。
条件が少ない最初の絞込みのときに、ToArray()かToList()などを使って、メモリに展開しておくのがよいようです。
クエリの複雑さにもよりますが、私のLet’s Note Y2 PentiumM1.5GHz+768MBのPCでは30倍以上の差が出ました。
これで、帳票作成のクエリにかかる時間が最大1分程度になったため、書き込み処理をブロックしてもそれほど業務に影響はないかな、と思ってとりあえず妥協しました。ただ、たまたま今回のプロジェクトではそれでよかったですが、読み込み中はぜんぜん書き込めないというのでは問題になることが多いのではないかなあ。
もっと直接的にコマンドを発行する方法もあるようなので、今度はそれで検討してみようか。
時期バージョンのVisual StudioではIronRubyが使えるらしいから、そのときはRailsのActiveRecordが使えるといいなあ。