2012年6月14日木曜日

ODP.NETへ移行


ハンディターミナルからのデータを.NET Frameworkで受け、Oracleに出力するプログラムがあるが、そのプログラムを新しい環境で動作することを検証している。4年以上前のプログラムで、実行環境もOracle 10g, .NET Framework 2.0で動作しているものを、Oracle 11g, .NET Framework 4.0で動作するかを確認した。
当初、問題無いだろうと思ったが、結構ハマったのでその記録。

ハンディターミナルからWindowsへの通信ができることが確認できたので、ハンディターミナルからデータを出力したところ、OCI-22053の例外が発生した。
調べたところ、Microsoft製のSystem.Data.OracleClientにバグがあり、そのバグは直さないと決めたそうだ。さらに、このモジュールは.NET Framework 4.0では非推奨にであるとのこと。
.NET Frameworkのバージョンを2.0にダウングレードしようと思ったが、このサーバーで4.0を対象にしたプログラムが動いているかもしれず、ダウングレードした時の動作は保証されないと言われ、やむなくダウングレードは諦めた。

今後はOracle製のモジュールを使用するようにとネットに書かれていたので、Oracle製のモジュールのODP.NET4をインストールした。幸い、プログラム自体はどのモジュールを使用するかは初期化ファイルに記述するようになっているので、初期化ファイルのプロバイダの値をSystem.Data.OracleClientからOracle.DataAccess.Clientに変更した。

今度はSELECT文でCONNECT BY PRIORしているテーブルを外部結合(+)した結果のフィールドがNULL以外のものを取得するSQL文であるが、NULLのものも結果セットに選択されているため、そのあとの動作がおかしくなっていた。
SELECT LV, ID
FROM (
SELECT LEVEL AS LV
, T1.ID
FROM T1, T2
WHERE T1.ID = T2.T1_ID (+)
START WITH T1.ID = (SELECT T1_ID FROM T3 WHERE ID = :ID)
CONNECT BY PRIOR T1.PARENT_ID = T1.ID
AND T1.ID != 0
)
WHERE ID IS NOT NULL;
ID IS NOT NULLとしているのに結果セットにはIDがNULLのレコードも含まれていた。

WITH句で取得した結果セットに対してNULL以外のものを取得するSQL文に変更した。
WITH T AS (
SELECT LEVEL AS LV
, T1.ID
FROM T1
LEFT OUTER JOIN T2
ON T2.T1.ID = T1.ID

START WITH T1.ID = (SELECT T1_ID FROM T3 WHERE ID = :ID)
CONNECT BY PRIOR T1.PARENT_ID = T1.ID
AND T1.ID != 0
)
SELECT LV
, ID
FROM T
WHERE ID IS NOT NULL;
プログラムを変更する必要があるため、開発環境を構築してプログラムをビルドした。

今度は通信はできるのだが、データベースの接続ができなくなった。調べてみると、ODP.NETはOracle Clientのtnsnames.oraの値を読んでいないためのようだ。
なので、接続文字列をORCLから(DESCRIPTION=以下の文字列に変更したところ、データベースの接続ができた。

すると、前回引っかかったところはクリアしたのだが、次のSQL文でORA-01008が出た。SQL文中のバインド変数とパラメータが一致しないということである。バインド変数の名前とパラメータの名前を比較したが、一致している。ググったところ、ODP.NETではデフォルトではバインドはパラメータの名前ではなく、パラメータの設定した順番に割り当てられるとのこと。同じパラメータを複数使用しているため、SQL文中のバインド変数とパラメータの個数が一致せずにエラーが発生していた。
バインド変数を名前でバインドさせるためにはOracleCommandのBindByNameプロパティをtrueにすればいいのだが、OracleCommandを使用しておらず、DbCommandを使用している。そのため、BindByNameを使用できない。今更OracleCommandに変更するわけにもいかず困っていたが、[Oracle][.NET]dbFactoryによる汎用的なDBアクセスで、ODP.NET独自のBindByNameプロパティを使うを見てDbCommandにBindByNameプロパティがあれば、trueを設定するようにしたところ、プログラムが正常に動作した。
.NET Frameworkにもリフレクションがあるんだ。
これでやっとテストができる。

0 件のコメント:

コメントを投稿