はじめに
自分は仕事で 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