Oracle Databaseの.NETアプリケーションの構築,
John Paul Cook著
Oracleデータベースを使用する.NETアプリケーションの構築に含まれる基本的な必須プロセスを学習します。
2005年10月更新
Microsoftの.NET Frameworkの人気が高まり、.NETアプリケーションとOracleの統合において、多くの開発者は基本的な接続性だけではなく、さらにVisual Studioを使用した効果的かつ効率的なアプリケーション開発での最適な手段に関する情報を求めています。
ここでは、Oracleデータベースを使用する.NETアプリケーションの構築に含まれる基本的な必須プロセスを説明します。以下の内容が含まれます。
単純なものからより複雑なものまで、難易度の異なる3つの実習で学習した内容を確認できます。
アプリケーションを保護する方法の詳細と実習について、詳しくは『 Oracleデータベースでのセキュアな.NETアプリケーション開発 』を参照してください。 (また、さまざまなアプリケーション・ライフ・サイクルの問題を記載した技術記事の『 Oracleを使用した.NETアプリケーション開発の習得』シリーズを参照してください。)
Oracle Technulogy Network(OTN)から ダウンロードできる無償のOracle Developer Touls for Visual Studioは、Oracleの.NETアプリケーションをさらに簡単かつ直観的に開発できるVisual Studioアドインを提供します。 この内容はここでは説明しません。詳しくはOracle Developer Touls for Visual Studio Product Center (US OTN、英語)を参照してください。
.NET Data Provider
.NETアプリケーションでは、Oracleの基本的なクライアント接続ソフトウェアに加えて、 マネージド・データ・プロバイダ(ここで、"マネージド"とは、.NETフレームワークで管理されるコードを指します)を使用する必要があります。 データ・プロバイダは、.NETアプリケーション・コードとOracleクライアント接続ソフトウェアの間のレイヤーです。 ほとんどの場合、汎用的な.NET ulE DBデータ・プロバイダの代わりに特定のデータベース・プラットフォームに最適化されたプロバイダを使用して、最適なパフォーマンスを実現します。
オラクル、Microsoft、およびサード・パーティ・ベンダーは、すべてOracleに最適化されたデータ・プロバイダを提供します。 オラクルとMicrosoftは、Oracleデータ・プロバイダを無償で提供しています(.NET Frameworkバージョン1.1用のMicrosoftのプロバイダは、フレームワークに含まれます。個別のダウンロードやインストールは必要ありません)。 サード・パーティのデータ・プロバイダは、古いバージョンのOracleをサポートします。Oracleクライアント・ソフトウェアをインストールする必要はありません。 ここでは、個別に ダウンロードできるOracle Data Provider for .NET(ODP.NET)の使用を前提とします。
ODP.NETおよびそのほかに必要なOracleクライアント接続ソフトウェアがインストールされると、Visual Studioを使用したアプリケーションの開発を始めることができます。 開発を始める前に、クライアントの接続性を確認することを推奨します。 Visual Studioと同じマシンでSQL*Plusを使用してOracleに接続できる場合は、Oracleのクライアント側ソフトウェアは正しくインストールされ、設定されていることになります。
Oracleを初めて使用する場合は、『 Oracle Data Provider for .NET開発者ガイド10g 』の"Oracle Databaseへの接続"の項でODP.NETに関する具体的なバックグラウンド情報を参照するか、『 Oracle Database管理者ガイド10g 』でOracleデータベースの一般的な管理に関する情報を参照してください。 『 How to: Connect to an Oracle Database Using ODP.NET (US OTN、英語)』ドキュメントのサンプル・コードを参考にすることもできます。
Visual Studioのプロジェクトの作成
Visual Studioの起動後、最初のタスクでプロジェクトを作成します。 次のように、「 新しいプロジェクト」ボタンをクリックするか、「 ファイル」→「 新規作成」→「 プロジェクト」を選択します。
図1:Visual Studioの新しいプロジェクトの作成
新しいプロジェクトダイアログ・ボックスが表示されます。 ダイアログ・ボックスの左側の プロジェクトの種類で、任意のプログラミング言語を選択します。 この例では、VB.NETが選択されました。 右側の テンプレートで、プロジェクト・テンプレートを選択します。 簡潔にするため、ここではWindows アプリケーションを選択します。
図2: 新しいプロジェクトダイアログの使用
プロジェクト名(ここではOtnWinApp)およびソリューション名(ここではOtnSamples)では意味のある名前を指定できます。 ソリューションには、1つ以上のプロジェクトが含まれます。 ソリューションにプロジェクトが1つだけ含まれる場合、多くのユーザーは両方に同じ名前を使用します。
参照の追加
プロジェクトはOracleデータベースに接続する必要があるため、選択したデータ・プロバイダを含んでいるdllへの参照を追加する必要があります。 ソリューション エクスプローラで、「 参照設定」ノードを選択し、右クリックして「 参照の追加」を選択します。 または、メニュー・バーへ移動し、「 プロジェクト」→「 参照の追加」の順に選択できます。
図3:参照の追加
参照の追加ダイアログ・ボックスが表示されます。
図4:ODP.NETマネージド・データ・プロバイダの選択
リストから「 Oracle.DataAccess.dll」を選択し、「 選択」ボタンをクリックします。「 OK」ボタンをクリックして、ODP.NETデータ・プロバイダとプロジェクトを関連づけます。
図5:Oracleマネージド・プロバイダ選択後のソリューション エクスプローラ
VB.NET/C#文
参照を追加したあとは、一般的にVB.NETの Imports文、C#の using文、またはJ#の import文を追加します。 技術的にこれらの文は必要ありませんが、長い完全修飾名を使用せずにデータベース・オブジェクトを参照できるようになります。
規則によって、名前空間やクラス宣言の前の、コード・ファイルの先頭にこれらの文をおきます。
Imports System.Data ' VB.NET
Imports Oracle.DataAccess.Client ' ODP.NET Oracle managed provider
using System.Data; // C#
using Oracle.DataAccess.Client; // ODP.NET Oracle managed provider
import System.Data.*; // J#
import Oracle.DataAccess.Client; // ODP.NET Oracle managed provider
接続文字列およびオブジェクト
Oracleの接続文字列は、Oracleの名前解決とは切り離せません。 以下のようなtnsnames.oraファイルに定義されたOracle Databaseのデータベース・エイリアスがあるとします。
OraDb=
(DESCRIPTION=
(ADDRESS_LIST=
(ADDRESS=(PROTOCOL=TCP)(HOST=OTNSRVR)(PORT=1521))
)
(CONNECT_DATA=
(SERVER=DEDICATED)
(SERVICE_NAME=ORCL)
)
)
上記のtnsnames.oraファイルに定義されたOracle Databaseエイリアスを使用するには、次の構文を使用します。
Dim oradb As String = "Data Source=OraDb;User Id=scott;Password=tiger;" ' VB.NET
string oradb = "Data Source=OraDb;User Id=scott;Password=tiger;"; // C#
ただし、接続文字列を変更して、tnsnames.oraファイルの必要性を排除できます。 エイリアス名をtnsnames.oraファイルに定義されているエイリアス名に置き換えてください。
' VB.NET
Dim oradb As String = "Data Source=(DESCRIPTION=" _
+ "(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=OTNSRVR)(PORT=1521)))" _
+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));" _
+ "User Id=scott;Password=tiger;"
string oradb = "Data Source=(DESCRIPTION=" // C#
+ "(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=OTNSRVR)(PORT=1521)))"
+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));"
+ "User Id=scott;Password=tiger;";
このように、ユーザー名とパスワードがクリアテキストとして接続文字列に組み込まれています。 これが接続文字列を作成するもっとも簡単な方法です。 ただし、このクリアテキストによるアプローチは、セキュリティの観点から推奨されていません。 とくに、コンパイルした.NETアプリケーション・コードはクリアテキストのソース・コード・ファイルよりもわずかに安全性が高いだけである点を理解する必要があります。 .NET dllおよびEXEファイルを逆コンパイルし、元のクリアテキストのコンテンツを表示することは非常に簡単です(暗号化は適切なソリューションですが、ここでのテーマから大きく外れます)。
次に、接続クラスの接続オブジェクトをインスタンス化する必要があります。 接続文字列と接続オブジェクトを関連づける必要があります。
Dim conn As New OracleConnection(oradb) ' VB.NET
OracleConnection conn = new OracleConnection(oradb); // C#
オーバーロードされるオブジェクトのコンストラクタで渡すことで、接続文字列と接続オブジェクトを関連づけるという点に注意してください。 コンストラクタのほかのオーバーロードによって、次の構文も使用できます。
Dim conn As New OracleConnection() ' VB.NET
conn.ConnectionString = oradb
OracleConnection conn = new OracleConnection(); // C#
conn.ConnectionString = oradb;
conn.Open() ' VB.NET
conn.Open(); // C#
Commandオブジェクト
Commandオブジェクトを使用して、SQL文字列またはストアド・プロシージャで実行されるSQLコマンドを指定します。 Connectionオブジェクトと同様に、クラスからインスタンス化する必要があります。また、オーバーロードしたコンストラクタが存在します。
Dim sql As String = "select dname from dept where deptno = 10" ' VB.NET
Dim cmd As New OracleCommand(sql, conn)
cmd.CommandType = CommandType.Text
string sql = "select dname from dept where deptno = 10"; // C#
OracleCommand cmd = new OracleCommand(sql, conn);
cmd.CommandType = CommandType.Text;
スカラー値の取得
DataReaderオブジェクトをインスタンス化し、OracleDataReaderオブジェクトを返す ExecuteReaderメソッドを使用することで、データベースからデータを取得できます。 VB.NET開発者は、列名またはゼロ・ベースの列の序数をItemプロパティに渡して、返されたデータにアクセスできます。 ほかには、列データを返すアクセッサ・メソッドを使用する方法があります。
Dim dr As OracleDataReader = cmd.ExecuteReader() ' VB.NET
dr.Read()
Label1.Text = dr.Item("dname") ' retrieve by column name
Label1.Text = dr.Item(0) ' retrieve the first column in the select list
Label1.Text = dr.GetString(0) ' retrieve the first column in the select list
OracleDataReader dr = cmd.ExecuteReader(); // C#
dr.Read();
label1.Text = dr.GetString(0); // C# retrieve the first column in the select list
整数の明示的な変換を以下に示します。
Label1.Text = CStr(dr.Item("deptno")) ' VB.NET integer to string cast
string deptno = dr.GetInt16("deptno").ToString(); // C#
CloseおよびDispose
接続オブジェクトの Closeまたは Disposeメソッドを呼び出して、データベースの接続を閉じる必要があります。 Disposeメソッドは、 Closeメソッドを呼び出します。
conn.Close() ' VB.NET
conn.Dispose() ' VB.NET
conn.Close(); // C#
conn.Dispose(); // C#
using (OracleConnection conn = new OracleConnection(oradb))
{
conn.Open();
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "select dname from dept where deptno = 10";
cmd.CommandType = CommandType.Text;
OracleDataReader dr = cmd.ExecuteReader();
dr.Read();
label1.Text = dr.GetString(0);
}
エラー処理
Try-Catch-Finally構造のエラー処理は、.NET言語の一部です。 Try-Catch-Finally構文を使用した比較的簡単な例を以下に示します。
Dim conn As New OracleConnection(oradb) ' VB.NET
Try
conn.Open()
Dim cmd As New OracleCommand
cmd.Connection = conn
cmd.CommandText = "select dname from dept where deptno = " + TextBox1.Text
cmd.CommandType = CommandType.Text
If dr.Read() Then
Label1.Text = dr.Item("dname") ' or use dr.Item(0)
End If
Catch ex As Exception ' catches any error
MessageBox.Show(ex.Message.ToString())
Finally
conn.Dispose()
End Try
OracleConnection conn = new OracleConnection(oradb); // C#
try
{
conn.Open();
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "select dname from dept where deptno = " + textBox1.Text;
cmd.CommandType = CommandType.Text;
if (dr.Read()) // C#
{
label1.Text = dr.GetString(0);
}
}
catch (Exception ex) // catches any error
{
MessageBox.Show(ex.Message.ToString());
}
finally
{
conn.Dispose();
}
図6:ユーザーに対して表示される検出されたORA-12545エラー
ORA-12545は、エンドユーザーではなくOracle DBAまたは開発者にとって大きな意味があります。 優れたソリューションは、 Catch文を追加してもっとも一般的なデータベース・エラーを取得し、わかりやすいメッセージを提供します。
Catch ex As OracleException ' catches only Oracle errors
Select Case ex.Number
Case 1
MessageBox.Show("Error attempting to insert duplicate data.")
Case 12545
MessageBox.Show("The database is unavailable.")
Case Else
MessageBox.Show("Database error: " + ex.Message.ToString())
End Select
Catch ex As Exception ' catches any error
MessageBox.Show(ex.Message.ToString())
catch (OracleException ex) // catches only Oracle errors
{
switch (ex.Number)
{
case 1:
MessageBox.Show("Error attempting to insert duplicate data.");
break;
case 12545:
MessageBox.Show("The database is unavailable.");
break;
default:
MessageBox.Show("Database error: " + ex.Message.ToString());
break;
}
}
catch (Exception ex) // catches any error
{
MessageBox.Show(ex.Message.ToString());
}
図7:ORA-12545エラーのわかりやすいエラー・メッセージ
エラーが発生したかどうかに関係なく、 Finallyコード・ブロックが常に実行されます。 Finallyコード・ブロックに接続オブジェクトの Closeまたは Disposeメソッドを配置すると、Try-Catch-Finallyコード・ブロックの実行後にデータベース接続が常に閉じられます。 開いていないデータベース接続を閉じようとしても、エラーは発生しません。 たとえば、データベースを使用できない場合、データベース接続が開かれないので、 Finallyコード・ブロックが存在しない接続を閉じようとします。 余分な Closeまたは Disposeを実行しようとする場合とは無関係です。 重要な点は、 Closeまたは Disposeメソッドを Finallyコード・ブロックに配置すると確実に接続が閉じられることです。
DataReaderを使用した複数の値の取得
これまで、単一の値を取得する例だけを説明してきました。 DataReaderを使用して、複数の列および行の値を取得できます。 最初に、複数の列の単一行の問合せを検討します。
select deptno, dname, loc from dept where deptno = 10
前の問合せのdname列およびloc列を連結するコードを以下に示します。
Label1.Text = "The " + dr.Item(1) + " department is in " + dr.Item("loc") ' VB.NET
Label1.Text = "The " + dr.GetString(1) + " department is in " + dr.GetString(2); // C#
select deptno, dname, loc from dept
While dr.Read() ' VB.NET
ListBox1.Items.Add("The " + dr.Item(1) + " department is in " + dr.Item("loc")) End While
while (dr.Read()) // C#
{
listBox1.Items.Add("The " + dr.GetString(1) + " department is in " + dr.GetString(2);
}
まとめ
ここでは、Visual Studioプログラミング言語を使用してOracleデータベースにアクセスするプロセスを紹介しました。 これで、データベースに接続して複数の列および行を取得できます。
John Paul Cook ( johnpaulcook@email.com)氏は、ヒューストン在住のデータベースおよび.NETのコンサルタントです。 .NETやOracleなどに関する多数の著述があり、1986年からリレーショナル・データベース・アプリケーションの開発に従事しています。 現在は、Visual Studio 2005やOracle 10g などに関心をもっています。 また、Oracle認定DBAおよびMicrosoft MCSD for .NETの資格を保有しています。最初に、ボタン・コントロールおよびラベル・コントロールをWindowsフォームに追加します。 コントロールの上に余白を残して、 実習2で追加できるようにします。
図8:ボタンおよびラベルのコントロールを使用した実習1のフォーム
図9:クリック・イベント・ハンドラのスタブ
Imports System.Data ' VB.NET
Imports Oracle.DataAccess.Client ' ODP.NET Oracle managed provider
using System.Data; // C#
using Oracle.DataAccess.Client; // ODP.NET Oracle managed provider
Dim oradb As String = "Data Source=(DESCRIPTION=(ADDRESS_LIST=" _
+ "(ADDRESS=(PROTOCOL=TCP)(HOST=OTNSRVR)(PORT=1521)))" _
+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));" _
+ "User Id=scott;Password=tiger;"
Dim conn As New OracleConnection(oradb) ' VB.NET
conn.Open()
Dim cmd As New OracleCommand
cmd.Connection = conn
cmd.CommandText = "select dname from dept where deptno = 10"
cmd.CommandType = CommandType.Text
Dim dr As OracleDataReader = cmd.ExecuteReader()
dr.Read()
Label1.Text = dr.Item("dname") ' or dr.Item(0)
conn.Dispose()
string oradb = "Data Source=(DESCRIPTION=(ADDRESS_LIST="
+ "(ADDRESS=(PROTOCOL=TCP)(HOST=OTNSRVR)(PORT=1521)))"
+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));"
+ "User Id=scott;Password=tiger;";
OracleConnection conn = new OracleConnection(oradb); // C#
conn.Open();
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "select dname from dept where deptno = 10";
cmd.CommandType = CommandType.Text;
OracleDataReader dr = cmd.ExecuteReader();
dr.Read();
label1.Text = dr.GetString(0);
conn.Dispose();
図10:正しく取得されたデータ
実習2:インタラクティビティの追加
データベース・アクセスの基本がコードに実装されたので、アプリケーションにインタラクティビティを追加します。 ハードコードされた問合せの代わりにテキストボックス・コントロールを追加して、部門番号(deptno)をユーザー入力として受け取ることができます。
図11:ボタンおよびラベルのコントロールを使用した実習2のフォーム
cmd.CommandText = "select dname from dept where deptno = " + TextBox1.Text 'VB.NET
cmd.CommandText = "select dname from dept where deptno = " + textBox1.Text; // C#
図12:未処理の例外
If dr.Read() Then ' VB.NET
Label1.Text = dr.Item("dname")
Else
Label1.Text = "deptno not found"
End If
if (dr.Read()) // C#
{
label1.Text = dr.GetString(0);
}
Else
{
label1.Text = "deptno not found";
}
アプリケーションでエラーを発生させる無効な入力を許可すべきではないという議論がありますが、最終的にはアプリケーションに強力なエラー処理を追加する必要があります。 すべてのエラーを防ぐことはできないので、エラー処理を実装する必要があります。
実習3:DataReaderを使用した複数の列および行の取得
単一の値を取得したので、DataReaderを使用して複数の列および行を取得します。 ListBoxコントロールをフォームに追加して、結果を表示します。
図13:ListBoxを追加したフォーム
cmd.CommandText = "select deptno, dname, loc from dept" ' VB.NET
cmd.CommandText = "select deptno, dname, loc from dept"; // C#
Dim oradb As String = "Data Source=(DESCRIPTION=(ADDRESS_LIST=" _
+ "(ADDRESS=(PROTOCOL=TCP)(HOST=OTNSRVR)(PORT=1521)))" _
+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));" _
+ "User Id=scott;Password=tiger;"
Dim conn As New OracleConnection(oradb) ' VB.NET
conn.Open()
Dim cmd As New OracleCommand
cmd.Connection = conn
cmd.CommandText = "select deptno, dname, loc from dept";
cmd.CommandType = CommandType.Text
Dim dr As OracleDataReader = cmd.ExecuteReader()
While dr.Read()
ListBox1.Items.Add("The " + dr.Item(1) + _
" department is in " + dr.Item("loc"))
End While
conn.Dispose()
string oradb = "Data Source=(DESCRIPTION=(ADDRESS_LIST="
+ "(ADDRESS=(PROTOCOL=TCP)(HOST=OTNSRVR)(PORT=1521)))"
+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ORCL)));"
+ "User Id=scott;Password=tiger;";
OracleConnection conn = new OracleConnection(oradb); // C#
conn.Open();
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = "select deptno, dname, loc from dept";
cmd.CommandType = CommandType.Text;
OracleDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
ListBox1.Items.Add("The " + dr.GetString(1) +
" department is in " + dr.GetString(2));
}
conn.Dispose();