OracleClientのバグ?
OracleClient周りで3日くらいずっと嵌っていたのですが、今日、やっと解決しました。現象からするとBCLのバグのよーな気がするのですが、どーなんでしょうか?
using System; using System.Data; using System.Data.OracleClient; public static class Program { // 以下のDDLでテーブルを作成しておく。 // create table users (name varchar2 (32), age integer); public static void Insert() { using (OracleConnection conn = new OracleConnection("Data Source=orcl;User Id=mei;Password=pass;")) { conn.Open(); OracleCommand cmd = new OracleCommand("INSERT INTO USERS VALUES('mei', 35)", conn); // 分離レベルを渡すと2回目以降、トランザクションが有効にならない。 // OracleTransaction tx = conn.BeginTransaction(IsolationLevel.ReadCommitted); // ↓このように引数無しなら問題なし。 OracleTransaction tx = conn.BeginTransaction(); cmd.Transaction = tx; try { cmd.ExecuteNonQuery(); } finally { // 必ずロールバックさせる。 tx.Rollback(); } } } public static void Main(string[] args) { Insert(); Insert(); Insert(); } } /* 結果 データが2行追加される。 つまり、最初の1件しかロールバックされない。 */
ってな感じです。
このコードだけなら3日も悩むようなモノに見えませんが、実際の現象はSeasar.NETを使ったパッケージ開発中に起り、疑う対象として、
- 自前のコード
- Seasar.NET
- BCL
があり、この順番に調べていったので最後の最後になってしまいました。(^^;
トランザクションはデータベース処理では最重要項目と言って良い部分で、こんなところでMSがミスっているとは思いもしなかったのですが、ちょっと信じられないレベルでバグってくれています。これが発見されないというのは、MSのテストケースもちょっとアレですが、.NETでOracleClientを使ってトランザクション処理やっているジョブって全然無いんだなぁ、と愕然となりました。多分、IDEからの自動生成を使って楽観的ロックだけでやっているんでしょうね・・・
私の記憶違いかもしれませんが、.NET2.0の頃は上記のコードが動作していたと思うので、.NET3.0あたりから混入したバグじゃないかと想像しています。もちろん、.NET3.5SP1でも直っていません。.NET3.5SP1で発生したバグという回答をMSからもらいました。
分離レベル周りは過去の記事でもIsolationLevel.Serializableが効かないことを指摘しましたが、OracleClientの実装はボロボロな気がしますね。
(追記)
Seasar.NETは引数ありのBeginTransactionを使っていますので、OracleClientを使うと大ピンチです。diconファイルでIsolationLevelを指定しなくても、Seasar内部ではデフォルトでReadCommittedを渡してしまっているので。なので、可能ならODP.NETを使うか、Seasar.NETのソースコードを書き換えるしかなさそうです。