開発者:.NET

Oracle Databaseの.NETアプリケーションの構築,

John Paul Cook著

Oracleデータベースを使用する.NETアプリケーションの構築に含まれる基本的な必須プロセスを学習します。

2005年10月更新

Microsoftの.NET Frameworkの人気が高まり、.NETアプリケーションとOracleの統合において、多くの開発者は基本的な接続性だけではなく、さらにVisual Studioを使用した効果的かつ効率的なアプリケーション開発での最適な手段に関する情報を求めています。

ここでは、Oracleデータベースを使用する.NETアプリケーションの構築に含まれる基本的な必須プロセスを説明します。以下の内容が含まれます。

  • .NETプロジェクトにOracleクラス・ライブラリをサポートする、プロジェクト参照の追加方法
  • Oracle Database接続文字列の作成方法
  • Connection、Command、およびDataReaderオブジェクトの操作方法

単純なものからより複雑なものまで、難易度の異なる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;
 
 
接続文字列を接続オブジェクトに関連づけたあと、 Openメソッドを使用して実際の接続をおこないます。

  
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;
 
 
異なるオーバーロードを使用すると、少し異なる構文を構築できます。 Commandオブジェクトには、コマンド・テキストを実行するメソッドがあります。 異なるタイプのSQLコマンドごとに異なるメソッドを使用することを推奨します。

スカラー値の取得

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
 
 
C#開発者は、データを取得するアクセッサ・メソッドを使用する必要があります。 .NETネイティブ・データ型またはネイティブのOracleデータ型を返す型指定アクセッサがあります。 ゼロ・ベースの序数をアクセッサに渡して、返す列を指定します。

  
OracleDataReader dr = cmd.ExecuteReader(); // C#
dr.Read();
label1.Text = dr.GetString(0); // C# retrieve the first column in the select list
 
 
簡素化されたこの例では、 dnameの戻り値(文字列)を使用して、ラベル・コントロールのテキスト・プロパティの値(文字列)を設定します。 ただし、文字列ではない deptnoが代わりに取得された場合、データ型は一致しません。 ソースと出力先のデータ型が一致しない場合、.NETランタイムは、特定のデータ型から別のデータ型に暗黙的に変換しようとします。 データ型に互換性がなく、暗黙的な変換に失敗して、例外がスローされる場合があります。 ただし、正常に処理される場合でも、暗黙的なデータ型変換ではなく明示的なデータ型変換を使用することを推奨します。

整数の明示的な変換を以下に示します。


  
Label1.Text = CStr(dr.Item("deptno")) ' VB.NET integer to string cast
 
 
C#は、VB.NETの暗黙的な変換ほど寛容ではありません。 明示的な変換を実行します。

  
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#
 
 
また、C#は、スコープ外になった場合に接続を自動的に破棄する特殊な構文を提供します。 usingキーワードでこの機能を使用できます。

  
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);
}
 
 
ここで学習した概念を 実習1(データベースのデータの取得)および 実習2(インタラクティビティの追加)で試すことができます。

エラー処理

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());
}
 
 
上記のコード例の2つの Catch文を確認してください。 検出するOracleエラーがない場合、最初の Catch文のブランチがスキップされ、2つ目の Catch文でほかのタイプのエラーが検出されます。 Catch文は、特定の設定から一般的な設定の順番でコードに配置する必要があります。 わかりやすい例外処理コードを実装したあと、次のようなORA-12545エラー・メッセージが表示されます。

図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
 
 
列の値を取得する場合、ゼロ・ベースの序数または列名を使用できます。 序数は、問合せの順序と関連します。 したがって、 dr.Item(2)または dr.Item("loc")を使用して、VB.NETでloc列の値を取得できます。

前の問合せの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
 
 
DataReaderから返される複数の行を処理するには、ループ構造が必要です。 また、複数の行を表示できるコントロールが求められます。 DataReaderは前方のみの読取り専用カーソルなので、Windows Forms DataGridコントロールなどの更新可能なコントロールや完全にスクロールできるコントロールにバインドできません。 次のコードに示されているように、DataReaderは、ListBoxコントロールと互換性があります。

  
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);
}
 
 
実習3(DataReaderを使用した複数の列および行の取得)は、これらの概念を示しています。

まとめ

ここでは、Visual Studioプログラミング言語を使用してOracleデータベースにアクセスするプロセスを紹介しました。 これで、データベースに接続して複数の列および行を取得できます。

John Paul Cook ( johnpaulcook@email.com)氏は、ヒューストン在住のデータベースおよび.NETのコンサルタントです。 .NETやOracleなどに関する多数の著述があり、1986年からリレーショナル・データベース・アプリケーションの開発に従事しています。 現在は、Visual Studio 2005やOracle 10g などに関心をもっています。 また、Oracle認定DBAおよびMicrosoft MCSD for .NETの資格を保有しています。

実習1:データベースのデータの取得

実習2:インタラクティビティの追加

データベース・アクセスの基本がコードに実装されたので、アプリケーションにインタラクティビティを追加します。 ハードコードされた問合せの代わりにテキストボックス・コントロールを追加して、部門番号(deptno)をユーザー入力として受け取ることができます。

  • 次のように、テキストボックス・コントロールおよび別のラベル・コントロールをフォームに追加します。 Label2コントロールのテキスト・プロパティを Enter Deptno:に設定して、 TextBox1の Textプロパティに何も設定されていないことを確認します。

    図11:ボタンおよびラベルのコントロールを使用した実習2のフォーム

  • SELECT文を定義するコードを変更します。
    
      
    cmd.CommandText = "select dname from dept where deptno = " + TextBox1.Text 'VB.NET
    
    cmd.CommandText = "select dname from dept where deptno = " + textBox1.Text; // C#
     
     
  • アプリケーションを実行します。 deptnoに10を入力して、アプリケーションをテストします。 無効なdeptno(50など)を入力して、アプリケーションを再度テストします。 アプリケーションが中断されます。

    図12:未処理の例外

  • コードを変更して、無効なdeptnoが入力された場合にエラーが発生しないようにします。 ExecuteReaderメソッドが実際にオブジェクトを返すことに注意してください。
    
      
    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";
    }
     
     
  • 存在しないdeptnoを入力して、アプリケーションをテストします。 アプリケーションが中断されなくなります。 数値の代わりにAを入力して、ボタンをクリックします。 アプリケーションが中断されます。 アプリケーションには、エラーを処理する効率的な方法が必要です。

    アプリケーションでエラーを発生させる無効な入力を許可すべきではないという議論がありますが、最終的にはアプリケーションに強力なエラー処理を追加する必要があります。 すべてのエラーを防ぐことはできないので、エラー処理を実装する必要があります。

実習3:DataReaderを使用した複数の列および行の取得

単一の値を取得したので、DataReaderを使用して複数の列および行を取得します。 ListBoxコントロールをフォームに追加して、結果を表示します。

  • ListBoxコントロールをフォームに追加します。 次のように、フォーム全体の幅に合わせてコントロールのサイズを調整します。

    図13:ListBoxを追加したフォーム

  • 問合せからwhere句を削除して、列を追加します。
    
      
    cmd.CommandText = "select deptno, dname, loc from dept" ' VB.NET
    
    cmd.CommandText = "select deptno, dname, loc from dept"; // C#
     
     
  • 次のように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;"
    
    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()
     
     
    次のようにC#コードを変更します。
    
      
    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(); 
     
     
ダウンロードできるコードには、エラー処理が実装されています。