はじめに
自分は仕事で Oracle データベースに接続する .NET アプリを書いています。と言っても、前任から引継したプログラムを改修しているので、データベースに接続するコードを自分で書いたことはありません。
客先の環境でエラー発生してデータベースに接続できないと、不具合の報告を貰うことがあります。同じプログラムが開発環境や別の端末で正しく動作するので、環境に依存するようです。
どんな環境は OK で、どんな環境は NG なのか、分からなくて苦しみました。当時の試行錯誤を、改めて確認しておこうと思いました。
Oracle.DataAccess.Client を使ってデータベースに接続する
前回の記事 で、System.Data.OracleClient または Oracle.ManagedDataAccess.Client を使ってデータベースに接続するプログラムを書きました。
仕事で引継したプログラムは、Oracle.DataAccess.Client を使ってデータベースに接続するようになっていました。
.NET Framework+Oracle.DataAccess.Client で接続する
.NET Framework アプリを作って Oracle データベースに接続してみます。
参考:【C#】【Oracle】Oracle.DataAccess.dllを利用してデータを取得。その1 | 創造的プログラミングと粘土細工
Visual Studio で「新しいプロジェクトの作成」して「コンソールアプリ (.NET Framework)」を選択します。
フレームワークは .NET Framework 4.5.2 を選択します。
System.Data.OracleClient と違って、Oracle.DataAccess.Client は、.NET Framework に含まれていません。
そこで、Oracle のウェブサイトから接続用ライブラリを入手します。
ODP.NET(管理対象外 )のインストール | 節約プログラマー雑記
その上で、プロジェクトの「参照」に Oracle.DataAccess.Client を追加します。
以下のコードを実行してみます。
using Oracle.DataAccess.Client;
string conStr = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL)));User id=scott;Password=tiger;";
try
{
using (OracleConnection cn = new OracleConnection())
{
cn.ConnectionString = conStr;
cn.Open();
Console.WriteLine("Connected.");
}
}
catch (Exception ex) {
Console.WriteLine("Error: " + ex.Message);
}
Console.ReadKey();
動作を確認する環境を用意する
作成した .NET アプリがデータベースに接続できるか確認する環境を用意します。仕事で使用していた環境に近いものを用意しようと思います。
・開発環境
Windows 10
Visual Studio 2017
ODAC 11g および 12c [32 ビット版] および [64 ビット版]
・接続元クライアント機
Windows 10 [64 ビット] および Windows XP [32 ビット]
Oracle Client 11g および 12c [32 ビット版] および [64 ビット版]
・接続先データベースサーバ
Windows 10
Oracle Database 11g XE
ODAC と Oracle Client
Oracle Data Access Components (ODAC) は、一連のWindowsおよび.NETデータ・アクセス・ドライバおよびツールです。.NET、COM および ODBC データ・アクセスのサポート、アプリケーションを開発するための Microsoft Visual Studio ツール、ASP.NET プロバイダ および .NET ストアド・プロシージャ・サポートが含まれます。
ODAC は Oracle が作成して配布しています。Oracle Client を入手してインストールすることで、開発機やクライアント機に導入できます。
Oracle Database 12c Client インストール - Project Group
開発機を準備する
開発機で、.NET アプリに組込する ODAC ライブラリは以下を切替して確認したい。
- 11g [32 ビット版]
- 11g [64 ビット版] ←今回は使わない
- 12c [32 ビット版]
- 12c [64 ビット版]
「OUI(Oracle Universanl Installer) 版」にしても「XCOPY 版」にしても、インストール先の Windows 環境の GAC やレジストリを更新する。一旦インストールすると、アンインストールしても Windows 環境の変更が完全に戻らない。
Oracle Client がインストールされたことのない Windows が、上記のパターンだけ欲しくなる。
実機を揃えるわけにはいかないし、クラウドサービスの仮想マシンを使ってもよかったのだが、1台の開発機で済ませてみた。
ODAC のライブラリ(実態は DLL ファイル)を、.NET アプリの「参照」に設定できればいいわけだ。
「XCOPY 版」のパッケージ(実態は ZIP ファイル)をダウンロードして解凍する。インストーラを使うとレジストリなど書込されてしまうので、解凍した内容をそのまま使う。
上記のパターンごとに 4 つのフォルダを用意して、使用したいライブラリを「参照」設定する。
クライアント機を準備する
接続元のクライアント機で、Oracle Client は以下を切替して確認したい。
- 11g [32 ビット版]
- 11g [64 ビット版]
- 11g [32 ビット版]+[64 ビット版] 混在 ←今回は使わない
- 12c [32 ビット版]
- 12c [64 ビット版]
- 12c [32 ビット版]+[64 ビット版] 混在
Oracle Client は、一旦インストールすると、アンインストールしても Windows 環境の変更が完全に戻らない。
Oracle Client がインストールされたことのない Windows が、上記のパターンだけ欲しくなる。
実機を揃えるわけにはいかないので困っていたが、Windows サンドボックス が使えるのでないかと思った。
Windows Sandboxを積極的に活用しよう|Kernel
ところが困ったことに、サンドボックスで起動した Windows で Oracle Client のインストーラを起動すると、「インストーラの検証実行に必要な最初の手順が失敗しました」エラーになってインストールできない。
Windows10にOracle Clientをインストールするとき詰まった話 #oracle - Qiita
サンドボックスの Windows アカウントは管理者権限がないのが問題のようだ。
実機を揃えるわけにはいかないので、クラウドサービスの仮想マシンを使うことにした。
Google Compute Engine を使って利用できる Windows 機は Windows Server なので、使いたかった Windows 10 と厳密に同じでないが仕方ない。
ところがそのせいか、インストーラが途中で落ちる。
(Oracle 12.1.0.2 32bit clientを導入する場合の障害について[2016/01/05])
苦労したが、複数の Oracle Client を導入したクライアント機を用意できた。
Windows XP [32 ビット] のクライアント機は、実機を用意した。
サーバ機を準備する
Oracle データベースをインストールするのは、開発機でもいいかと思ったが、アプリに組込するライブラリに影響するといけないので、別に準備した。
当時の環境は Windows Server 2012 R2 だったが、空いていた Windows 10 機を使った。
Oracle Database 11g は、無償版の Express Edition を使った。
併せて、Oracle Database 21c Express Edition も用意した。
こちらはインストールして途中でエラー表示されたりして非常に時間が掛かった。
参考:OracleDatabase21cExpressEditionをイ... - Yahoo!知恵袋
Oracle Client 11g や 12c で Oracle Database 21c に接続できるか確認してみたが、OK だった。
Oracle.DataAccess.Client でデータベースに接続する①
前述した手順で Oracle データベースに接続する .NET アプリを作成します。
.NET アプリに組込する Oracle.DataAccess ライブラリは 12c [64 ビット版] を選びました。
開発機やクライアント機が [64 ビット] 機 だからです。
接続先のデータベースは 11g ですが、新しいバージョンがいいだろうと考えました。
Oracle.DataAccess 12c [64]+Windows 10 [64]+Oracle Client なし・・①
まず、Oracle Client をインストールしていないクライアント機で実行してみます。
'Oracle.DataAccess.Client.OracleConnection' のタイプ初期化子が例外をスローしました。
動作:×
この .NET アプリは開発機で動作しています。違うのは、開発機は Oracle Client がインストールされていることです。
Oracle.DataAccess は Oracle.ManagedDataAccess と違って、Oracle データベースと直接接続しないで、OS にインストールされた Oracle Client を呼出しているようです。
Oracle.DataAccess 12c [64]+Windows 10 [64]+Oracle Client 12c [64]・・②
上記のクライアント機に Oracle Client 12c [64 ビット版] をインストールします。
接続できました。
動作:○
Oracle.DataAccess.Client でデータベースに接続する②
一部のクライアント機は Windows XP [32 ビット版] が残っていると、顧客に言われました。
Oracle.DataAccess 12c [64]+Windows XP [32]+Oracle Client 12c [32]・・③
クライアント機に導入するのは Oracle Client 12c [32 ビット版] になります。
作成した .NET アプリはそのままで実行してみます。
'Oracle.DataAccess.Client.OracleConnection' のタイプ初期化子が例外をスローしました。
動作:×
Oracle.DataAccess 12c [32]+Windows XP [32]+Oracle Client 12c [32]・・④
.NET アプリに組込するライブラリを [32 ビット版] に変更してみます。
接続できました。
動作:○
Oracle.DataAccess.Client でデータベースに接続する③
大半のクライアント機は Windows 10 [64 ビット版] です。
Oracle.DataAccess 12c [32]+Windows 10 [64]+Oracle Client 12c [32]・・⑤
クライアント機にインストールする Oracle Client を、.NET アプリに組込したライブラリに合わせて 12c [32 ビット版] に変更しました。
ハンドルされていない例外: System.BadImageFormatException: ファイルまたはアセンブリ 'Oracle.DataAccess, Version=4.121.2.0, Culture=neutral, PublicKeyToken=89b483f429c47342'、またはその依存関係の 1 つが読み込めませんでした。間違ったフォーマットのプログラムを読み込もうとしました。
動作:×
Oracle.DataAccess 12c [32]+Windows 10 [64]+Oracle Client 12c [64]・・⑥
クライアント機にインストールする Oracle Client を、クライアント機に合わせて [64 ビット版] に変更しました。
接続できました。
動作:○
.NET アプリに組込したライブラリとクライアント機に導入する Oracle Client は一致していなくていいのでしょうか。
Oracle.DataAccess.Client でデータベースに接続する④
引継した .NET アプリの一部はサードパーティのライブラリを組込していました。そのライブラリは [32 ビット版] だけで [64 ビット版] が提供されていませんでした。
[32 ビット版] Windows で実行できればいいのですが、[64 ビット版] Windows で実行しないといけません。
プログラムは動作を停止しました。
.NET アプリを [32 ビット] モード で実行すればいいようです。.NET アプリの「ターゲット CPU」の指定が AnyCPU だったものを x86 に変更しました。
.NETにおける64ビットプロセスと32ビットプロセスについて #Windows - Qiita
x86 指定+Oracle.DataAccess 12c [32]+Windows 10 [64]+Oracle Client 12c [64]・・⑦
x86 指定してビルドし直した .NET アプリを実行してみます。
'Oracle.DataAccess.Client.OracleConnection' のタイプ初期化子が例外をスローしました。
動作:×
x86 指定+Oracle.DataAccess 12c [32]+Windows 10 [64]+Oracle Client 12c [32]・・⑧
クライアント機にインストールする Oracle Client を、.NET アプリの実行モードに合わせて [32 ビット版] に変更しました。
接続できました。
動作:○
Oracle.DataAccess.Client でデータベースに接続する⑤
一部の .NET アプリは Windows Server 2003 で実行しないといけなくなりました。
Oracle Client 12c は Windows Server 2003 Server で動作しませんでした。
参考:Oracle Database Clientインストレーション・ガイド12cリリース1(12.1) for Microsoft Windows
Oracle Client 11g なら Windows Server 2003 Server で動作するようです。
参考:Oracle Database Clientインストレーション・ガイド, 11g リリース2 (11.2) for Microsoft Windows
x86 指定+Oracle.DataAccess 12c [32]+Windows 10 [64]+Oracle Client 11g [32]・・⑨
クライアント機にインストールする Oracle Client を 11g にしました。
ハンドルされていない例外: System.BadImageFormatException: ファイルまたはアセンブリ 'Oracle.DataAccess, Version=4.121.2.0, Culture=neutral, PublicKeyToken=89b483f429c47342'、またはその依存関係の 1 つが読み込めませんでした。間違ったフォーマットのプログラムを読み込もうとしました。
動作:×
x86 指定+Oracle.DataAccess 11g [32]+Windows 10 [64]+Oracle Client 11g [32]・・⑩
.NET アプリに組込するライブラリを、Oracle Client に合わせて 11g に変更しました。
接続できました。
動作:○
x86 指定+Oracle.DataAccess 11g [32]+Windows 10 [64]+Oracle Client 12c [32]・・⑪
クライアント機にインストールする Oracle Client を 12c に戻してみました。
接続できました。
動作:○
Oracle.DataAccess.Client でデータベースに接続する⑥
上記のクライアント機で Oracle Client が 32 ビット版 に加えて 64 ビット版 が必要になりました。
Microsoft Access で ODBC 経由で Oracle データベースに接続しているそうです。Access が 32 ビット版 のときは Oracle Client(それに含まれる ODBC ドライバ)は 32 ビット版 が必要で、Access が 64 ビット版 になると ODBC ドライバ 64 ビット版 が必要になるそうです。
x86 指定+Oracle.DataAccess 11g [32]+Windows 10 [64]+Oracle Client 12c [32]+[64]・・⑫
Oracle Client 32 ビット版 と 64 ビット版 が混在する環境になりました。
接続できました。
動作:○
x86 指定+Oracle.DataAccess 11g [32]+Windows 10 [64]+Oracle Client 12c [32]+[64]←64 指定・・⑬
別のアプリがデータベースに接続できなくなったと顧客から報告ありました。調べたところ ORACLE_HOME の指定すれば直るようです。
Oracle Client [64 ビット版] のインストール先を ORACLE_HOME に指定しました。
ORA-12557: TNS: プロトコル・アダプタをロードできません
動作:×
x86 指定+Oracle.DataAccess 11g [32]+Windows 10 [64]+Oracle Client 12c [32]+[64]←32 指定・・⑭
Oracle Client [32 ビット版] のインストール先を ORACLE_HOME に指定しました。
接続できました。
動作:○
x86 指定+Oracle.DataAccess 11g [32]+Windows 10 [64]+Oracle Client 12c [64]・・⑮
.NET アプリに組込するライブラリは [32 ビット版] +クライアント機の Oracle Client は [64 ビット版] で OK でした(上記⑥)。[x86] 指定 した .NET アプリは、どうなるでしょうか。
'Oracle.DataAccess.Client.OracleConnection' のタイプ初期化子が例外をスローしました。
動作:×
.NET アプリが [32 ビット] モード で実行されるなら、呼出される Oracle Client は [32 ビット版] でなければならないようです。
確認の結果のまとめ
以上の試行錯誤を表にまとめてみます。
| Oracle.DataAccess | OS | Oracle client | 動作 | ||
|---|---|---|---|---|---|
| ① | AnyCPU | 12c [64] | Windows 10 [64] | なし | × |
| ② | AnyCPU | 12c [64] | Windows 10 [64] | 12c [64] | ○ |
| ③ | AnyCPU | 12c [64] | Windows XP [32] | 12c [32] | × |
| ④ | AnyCPU | 12c [32] | Windows XP [32] | 12c [32] | ○ |
| ⑤ | AnyCPU | 12c [32] | Windows 10 [64] | 12c [32] | × |
| ⑥ | AnyCPU | 12c [32] | Windows 10 [64] | 12c [64] | ○ |
| ⑦ | x86 | 12c [32] | Windows 10 [64] | 12c [64] | × |
| ⑧ | x86 | 12c [32] | Windows 10 [64] | 12c [32] | ○ |
| ⑨ | x86 | 12c [32] | Windows 10 [64] | 11g [32] | × |
| ⑩ | x86 | 11g [32] | Windows 10 [64] | 11g [32] | ○ |
| ⑪ | x86 | 11g [32] | Windows 10 [64] | 12c [32] | ○ |
| ⑫ | x86 | 11g [32] | Windows 10 [64] | 12c [32]+[64] | ○ |
| ⑬ | x86 | 11g [32] | Windows 10 [64] | 12c [32]+[64]←64 | × |
| ⑭ | x86 | 11g [32] | Windows 10 [64] | 12c [32]+[64]←32 | ○ |
| ⑮ | x86 | 11g [32] | Windows 10 [64] | 12c [64] | × |
ルールが分かってくればまだしも当時は分かっていなかったので、作成している .NET アプリと別の都合で実行環境が変化するたびにデータベースに接続できなくなって、対応に悩まされました。
Oracle.ManagedDataAccess.Client を使って接続する
Oracle.DataAccess ライブラリを使うと上記のように、インストールする Oracle Client の状況に依存して、同じ .NET アプリが接続できたりできなくなったりします。
これを回避するために、どうしたらいいでしょうか。
Oracle.ManagedDataAccess ライブラリを使えばいいようです。
【.net】Oracleクライアントインストールなしで接続する方法 | 水戸スヤのSE備忘録
Oracle.ManagedDataAccess は Oracle.DataAccess と違って、OS にインストールされた Oracle Client を使わずに、Oracle データベースと直接接続するようです。
引継したプログラムが Oracle.DataAccess ライブラリを使っていたので、そのまま使い続けてしまいましたが、Oracle.ManagedDataAccess ライブラリに切替するのは、簡単だったようです。
ODP.NET Managed Driver(管理対象ドライバ)についての私的まとめ - しばたテックブログ
接続先を記述する
前回の記事 で、接続先などを指定する接続文字列は以下のように書いています。
string conStr = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL)));User id=scott;Password=tiger;";
参考:ODP.NET の接続文字列 - tsucchi’s diary(元はてなダイアリー)
仕事で引継したプログラムを見ると、Data Source は上記のように書かれていませんでした。
string conStr = "Data Source=ORCL;User id=scott;Password=tiger;";
ここで指定された ORCL では接続先が特定できません。これは「ネットサービス名」で、別途 tnsnames.ora ファイルに設定を記述しておいて、ライブラリが設定された内容を読みます。
参考:tnsnames.oraと接続文字列の関係 #oracle - Qiita
ORCL = (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL)))
これでは見づらいのでインデントします。
ORCL =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = ORCL)
)
)
この tnsnames.ora は、どこに置けばいいのでしょうか。
Oracle Client をインストールして Net Manager ツールを使うと、GUI で tnsnames.ora を所定の場所に作成してくれます。.NET アプリの Oracle Client ライブラリは、その場所に保存された tnsnames.ora を読んでくれるようです。
ただし、Oracle Client を複数インストールすると、どの設定が読まれるのか、にわかに分からなくなります。
参考:【考察】Oracle Clientを何度もインストールするとどこのtnsnames.oraを使っているかわからなくなるという話 #Windows - Qiita
tnsnames.ora ファイルは、.NET アプリの実行ファイルと同じフォルダに置けば、それが読込されます。
参考:Managed な ODP.NET で tnsnames.ora を参照する (Windows, Visual Studio 2015) #oracle - Qiita
