次の方法で共有


ADO.NET の使用

 

ポール・D・シェリフ
PDSA, Inc.

2002 年 1 月

の概要: ADO.NET Connection オブジェクト、その他の ADO.NET オブジェクトの使用について説明し、例を示します。 (41ページ印刷)

目標

  • Microsoft® ADO.NET Connection オブジェクトを作成する方法について説明します
  • ADO.NET Command オブジェクトを使用して SQL を送信する
  • ADO.NET DataReader クラスを使用する
  • リスト ボックスにオブジェクトを読み込む方法について説明します
  • DataTable オブジェクトと DataSet オブジェクトを使用する
  • DataSet オブジェクトを使用してデータを変更する

仮定

このドキュメントを最大限に活用するには、次のことが当てはまります。

  • リレーショナル データベースを理解する
  • ADO に慣れている
  • Microsoft Windows® フォームをビルドする方法を理解している
  • Microsoft SQL Server™ やその他のデータベース (Microsoft Access など) に慣れ、アクセスできます。

内容

ADO.NET 接続
ADO.NET コマンド オブジェクト
ADO.NET DataReader の使用
ADO.NET DataTable と DataSet の使用
Visual Basic 6.0 と ADO の違い
概要

ADO.NET 接続

ADO.NET Connection オブジェクトを使用して、プログラムとデータベース エンジン間の接続を作成します。 通常、この接続は、データを取得または更新するのに十分な長さだけ開いたままにします。 接続をすばやく開いてから閉じることで、できるだけ短時間サーバー リソースを使用します。 これにより、リソースに優しいスケーラブルで高速なアプリケーションを開発できます。 使用するリソースが少ないほど、一度にアプリケーションでサポートできるユーザーが増えます。

データベース アプリケーションを作成する場合は、最終的にそのデータベースへの接続を開く必要があります。 ADO.NET Connection オブジェクトを使用すると、その接続を作成できます。 また、データベース内のデータを取得および変更する必要があり、ここで ADO.NET Command オブジェクトを使用する必要があります。

SQL Server 7.0 以降に接続するときは、System.Data.SqlClient 名前空間の SqlConnection オブジェクトと SqlCommand オブジェクトを使用します。 他の OLE DB データソースに接続する場合は、System.Data.OleDb 名前空間で OleDbConnection と OleDbCommand を使用します。 このドキュメントの残りの例では、System.Data.SqlClient 名前空間のオブジェクトを使用した例を示します。

データベース 内のデータを変更するには

  1. SqlConnection または OleDbConnection オブジェクトを作成し、接続文字列を指定します。
  2. データベースへの接続を開きます。
  3. SqlCommand または OleDbCommand オブジェクトを作成し、開いた接続オブジェクトを割り当てます。
  4. SQL ステートメントを Command オブジェクトに配置します。
  5. Command オブジェクトで ExecuteNonQuery メソッドを呼び出して、SQL ステートメントを実行します。

接続オブジェクトとコマンド オブジェクト

このドキュメントでは、SqlConnection クラスを使用して SQL Server データベースへの接続を作成して開く方法について説明します。 さらに、SqlCommand オブジェクトを使用して、同じ SQL Server データベースに INSERT ステートメントを送信する方法について説明します。

SQL Server データベースへの接続を開く方法については、次の手順に従います。 図 1 は、接続のテストに使用するサンプル フォームを示しています。 次の手順に従って、このフォームを作成します。

図 1: SQL Server データベースへの SQL ステートメントの送信

サンプル フォーム を作成するには

  1. Microsoft Visual Studio® .NET を開きます。

  2. [新しいプロジェクトクリックします。

  3. 左側のツリー ビューで、Visual Basic プロジェクト選択します。

  4. プロジェクト テンプレート ウィンドウで、Windows アプリケーション選択します。

  5. を DataConnectに設定します。

  6. [OK]クリックします。

  7. Form1.vb という名前のフォームの名前を frmConnect.vbに変更します。

  8. このフォームの Text プロパティを SQL Tester設定します。

  9. 表 1 を参照して、このフォームのコントロールを作成します。

    表 1. ADO.NET オブジェクトをテストするフォームをビルドするコントロール

    コントロールの種類 財産 価値
    CommandButton 名前 btnConnect
      テキスト 繋ぐ
    TextBox 名前 txtSQL
      MultiLine
    ラベル 名前 Label1
      テキスト 影響を受ける行
    TextBox 名前 txtRows
      テキスト  
      ReadOnly
    CommandButton 名前 btnExecute
      テキスト SQL の実行

次に、データベースに接続するためのコードを記述しましょう。

  1. 接続をダブルクリックします。

  2. 次に示すコードを btnConnect_Click イベント プロシージャに追加します。

    Private Sub btnConnect_Click( _
     ByVal sender As System.Object, _
     ByVal e As System.EventArgs) Handles btnConnect.Click
        Dim oConn As SqlClient.SqlConnection
        Dim strConn As String
    
        Try
            ' Create the Connection object
            oConn = New SqlClient.SqlConnection()
    
            ' Build the connection string      
            strConn &= "Data Source=(local);"
            strConn &= "Initial Catalog=Northwind;"
            strConn &= "User ID=sa;"
            strConn &= "Password=;"
    
            ' Set the Connection String
            oConn.ConnectionString = strConn
    
            ' Open the Connection
            oConn.Open()
    
            MessageBox.Show("Connection Open", _
             "btnConnect_Click()")
    
            ' Close the Connection
            oConn.Close()
    
        Catch oExcept As Exception
            MessageBox.Show(oExcept.Message, _
             "btnConnect_Click()")
    
        End Try
    
    End Sub
    

このイベント プロシージャでは、まず SqlConnection クラスの新しいインスタンスを作成します。 次に、接続を開く前に ConnectionString プロパティを入力します。

以前のバージョンの ADO では、ConnectionTimeout プロパティを 0 から n の値に設定することができました。 これは、例外がスローされるまでの接続を待機する時間を表します。 ADO.NET では、このプロパティは読み取り専用です。 ConnectionTimeout を設定するには、プロバイダー文字列 Connect Timeout=n を渡します。 さらに、SqlConnection オブジェクトのプロパティとして、DataSource、Database などの個々のプロパティを設定しなくなりました。 これらの値は読み取り専用になり、プロバイダー文字列から解析された値が反映されます。

接続文字列

次に、SQL Server データベースを指す接続文字列を作成します。 接続文字列には、セミコロンで区切られた属性のセットがあります。 使用する必要がある .NET データ プロバイダーの種類と、データベース システムの種類ごとに設定される属性に応じて、各 .Net データ プロバイダー接続文字列の外観が異なります。 たとえば、次の接続文字列は、ローカル SQL Server への接続に使用する接続文字列の例です。

Data Source=(local);Initial Catalog=Northwind;User ID=sa;Password=;

次に示す接続文字列は、System.Data.OleDb の OleDbConnection オブジェクトを使用して Microsoft Access 2000 データベースに接続する方法の例です。

Provider=Microsoft.Jet.OleDb.4.0;Data Source=C:\Northwind.mdb

別のデータベース エンジンを使用している場合は、特定のエンジンに対して設定する適切な属性を検索する必要があります。 さまざまなプロバイダーの接続文字列の設定の詳細については、ADO.NET ヘルプを参照してください。

文字列変数 strConn にこの接続文字列を作成し、その変数を Connection オブジェクトの ConnectionString プロパティに割り当てます。

ConnectionString プロパティが設定されたら、Connection オブジェクトで Open メソッドを呼び出します。 これにより、接続オブジェクトが指定されたプロバイダーを読み込み、データ ソースへの接続を開きます。 指定した場合、ユーザー ID 属性とパスワード属性を使用してデータ ソースにログインします。 初期カタログ属性は、既定のデータベースをこの属性で指定されたデータベースにするように接続に指示します。

接続が完了したら、常に接続を閉じる必要があります。 入力したコード例では、接続を開いてすぐに閉じます。 通常、接続を閉じる前に、接続に対して何らかの操作を実行します。 これについては、次のセクションで説明します。

試してみる

  1. プロジェクトを実行するには、F5押します。
  2. [接続 クリックして、入力したコードを実行します。 すべてが正しく設定されている場合は、接続が開かれたことを示すメッセージ ボックスが表示されます。

接続を開く方法がわかったら、おそらくそれを使って何かをしたいと思うでしょう。 以前のバージョンの ADO とは異なり、ADO.NET では接続オブジェクトに対して SQL ステートメントを直接実行することはできません。 SQL ステートメントを送信するには、常に Command オブジェクトを使用します。

ADO.NET コマンド オブジェクト

Command オブジェクトは、古い ADO コマンド オブジェクトによく似ています。 データ ソースに対して実行する必要がある SQL ステートメントを格納するために使用されます。 Command オブジェクトは、SELECT ステートメント、INSERT、UPDATE、DELETE ステートメント、ストアド プロシージャ、またはデータベースで認識されるその他のステートメントを実行できます。 図 1 では、Northwind データベースに対して実行して Customers テーブルに行を追加できる INSERT ステートメントの例を見ました。 ここでは、その INSERT ステートメントを実行するコードを記述する方法について説明します。

  1. frmConnect.vb フォームを開きます。

  2. [SQLの実行 ダブルクリックします。

  3. btnExecute_Click イベント プロシージャで次に示すコードを記述します。

    Private Sub btnExecute_Click(ByVal sender As Object, _
     ByVal e As System.EventArgs) Handles btnExecute.Click
        Dim oCmd As SqlClient.SqlCommand
        Dim strConn As String
    
        Try
            ' Build the connection string
            strConn &= "Data Source=(local);"
            strConn &= "Initial Catalog=Northwind;"
            strConn &= "User ID=sa;"
            strConn &= "Password=;"
    
            ' Create the Command Object
            oCmd = New SqlClient.SqlCommand()
            ' Assign Connection to Command Object
            oCmd.Connection = _
             New SqlClient.SqlConnection(strConn)
            ' Open the Connection
            oCmd.Connection.Open()
            ' Assign the SQL to the Command Object
            oCmd.CommandText = txtSQL.Text
            ' Execute the SQL, 
            ' Return Number of Records Affected
            txtRows.Text = _
             oCmd.ExecuteNonQuery().ToString()
    
            MessageBox.Show("SQL statement succeeded", _
             "btnExecute_Click()")
    
            ' Close the Connection      
            oCmd.Connection.Close()
    
        Catch oExcept As Exception
            txtRows.Text = 0.ToString()
            MessageBox.Show("Error executing SQL: " & _
             oExcept.Message, "btnExecute_Click()")
    
        End Try
    End Sub
    

btnExecute_Click イベント プロシージャでは、Command オブジェクトを参照する oCmd という名前の変数を宣言する必要があります。 次に、データ ソースへの接続を作成するために使用する接続文字列を作成します。 次に、Command オブジェクトをインスタンス化し、その Connection プロパティを New Connection オブジェクトに設定します。 この新しい Connection オブジェクトを作成したら、接続を開くことができます。

接続が開いたら、CommandText プロパティに、データベースに送信する SQL ステートメントを入力できます。 この場合、SQL ステートメントはフォーム上の テキスト ボックス から取得されます。

コマンド オブジェクトの ExecuteNonQuery メソッドを呼び出して、SQL をバックエンド データベースに送信します。 INSERT、UPDATE、DELETE ステートメントなど、結果を返さないコマンドを実行する場合は、ExecuteNonQuery メソッドを使用します。 これらの種類のクエリは結果セットとして行を返しません。単にデータを変更し、影響を受ける行数を返します。 このメソッドは、送信された SQL ステートメントの影響を受ける行数を返します。 txtRows テキスト ボックスの Text プロパティに配置できるように、この値を文字列に変換します。 最後に、Connection プロパティの Close メソッドを使用して接続を閉じます。

ADO とは異なり、接続オブジェクトがスコープ外になって接続が閉じられるまで待機できますが、ADO.NET では、この手順に示すように常に接続を明示的に閉じる必要があります。 接続で Close() を呼び出すと、データベース接続がプールに解放され、他のプロシージャができるだけ早く再利用されます。

試してみる

この新しいボタンを試すには、Northwind データベース内のいずれかのテーブルに対して INSERT、UPDATE、または DELETE ステートメントを記述し、データベースに送信します。

  1. F5キー 押してプログラムを実行します。
  2. 有効な INSERT、UPDATE、または DELETE ステートメントを入力します。 必要に応じて、テキスト ボックスに既に配置されている INSERT ステートメントを例として使用できます。
  3. [SQL実行] をクリックします。

すべてが機能する場合は、ボタンの左側に作成したテキスト ボックスに、影響を受ける行の数が表示されます。

ADO.NET DataReader の使用

ADO.NET では、レコードセットがなくなりました。 代わりに、データ ソースからレコードを取得するために使用される DataSet、DataTable、DataReaders などの新しいオブジェクトがあります。 次に、DataReader オブジェクトについて学習します。 DataTable と DataSet については、このドキュメントの後半で説明します。

DataReader オブジェクトは、データ ソースからレコードを取得する最速の方法を提供する前方専用のカーソルです。 その方向は前方専用に制限されているため、プログラムで結果を処理したり、リスト ボックスやコンボ ボックスなどを読み込んだりするための優れたパフォーマンスを提供します。

DataReader を使用してリスト ボックスを読み込む

このセクションでは、DataReader オブジェクトを使用して Products テーブルのデータを含むリスト ボックスを読み込みます。 また、リスト ボックスをクリックすると、1 つのレコードが取得されます。 図 2 は、製品情報の表示、追加、編集、削除に使用できる単純なデータ入力画面を示しています。

図 2: 一般的なクライアント/サーバー データ入力画面

Northwind データベースの Products テーブル内のすべての製品の名前を含むリスト ボックスを読み込むには、次の手順に従います。

  1. 図 2 のようなフォームを作成します。 このフォームの名前を frmProducts.vbに設定します。 リスト ボックスの名前は lstProductsです。

  2. ソリューション エクスプローラーで、frmProducts.vb ファイルをダブルクリックして製品フォームを表示します。

  3. フォーム上の任意の場所をダブルクリックします (コントロールをクリックしていないことを確認してください)。 frmProduct_Load イベント プロシージャがコード ウィンドウに表示されます。

  4. この Load イベント プロシージャ内で、次の手順で記述する ListLoad プロシージャを呼び出します。 手順は、次に示すコードのようになります。

    Private Sub frmProduct_Load( _ 
     ByVal sender As System.Object, _
     ByVal e As System.EventArgs) Handles MyBase.Load
            ListLoad()
    End Sub
    
  5. frmProduct_Load イベント プロシージャの End Sub のすぐ下に ListLoad プロシージャを作成し、この ListLoad プロシージャに次のコードを入力します。

    Private Sub ListLoad()
        Dim oCmd As SqlClient.SqlCommand
        Dim oDR As SqlClient.SqlDataReader
        Dim strSQL As String
        Dim strConn As String
    
        strConn = ConnectStringBuild()
    
        strSQL = "SELECT ProductName "
        strSQL &= "FROM Products"
    
        Try
            oCmd = New SqlClient.SqlCommand()
            With oCmd
                .Connection = _
                 New SqlClient.SqlConnection(strConn)
                .Connection.Open()
                .CommandText = strSQL
                oDR = .ExecuteReader()
            End With
    
            lstProducts.Items.Clear()
            Do While oDR.Read()
                lstProducts.Items.Add(oDR.Item("ProductName"))
            Loop
    
        Catch oExcept As Exception
            MessageBox.Show(oExcept.Message)
    
        End Try
    End Sub
    

上記のコードでは、Command 用と DataReader 用の 2 つのオブジェクトを宣言しています。 Command オブジェクトは、SELECT ステートメントを保持し、データ ソースに送信するために実行します。 DataReader は、SELECT ステートメントから返される結果セットからデータを取得するオブジェクトです。

Command オブジェクトの Connection プロパティを Connection オブジェクトの新しいインスタンスに設定します。 この新しい Connection オブジェクトに接続文字列を渡します。このセクションの後半で、独自の ConnectStringBuild 関数をビルドします。 適切な接続文字列が設定されたら、コマンド オブジェクトで接続を開くことができます。

SELECT ステートメントを CommandText プロパティに配置します。 ExecuteReader メソッドを呼び出すと、コマンド オブジェクトは SELECT ステートメントをバックエンド データ ソースに送信します。 結果が返され、DataReader オブジェクトにこの結果セットへのポインターが渡されます。 カーソルは、この結果セットの最初のレコードの直前に設定されます。

Read メソッドを呼び出して、DataReader 内の各行をループ処理します。 Read メソッドは、カーソルを 1 つの行から次の行に移動します。 Read メソッドが実行されたら、取得する列の名前を DataReader の Item プロパティに渡すことができます。 これにより、その列の実際のデータが返されます。

Item プロパティから返されるデータは、Object データ型として返されます。 リスト ボックスに追加する項目は Object 型であるため、リスト ボックスの Items コレクションの Add メソッドを使用する場合、変換は必要ありません。

この方法では、Read メソッドが False を返すまでループを続行します。 これは、SELECT ステートメントから返されたデータ行の末尾に達したことを意味します。

接続文字列を作成する

このルーチンを試して、データがリスト ボックスに正しく読み込まれているかどうかを確認するには、接続文字列をビルドする必要があります。

  1. ListLoad プロシージャの End Sub の直後に、ConnectStringBuild という名前の新しいプロシージャを追加します。

  2. 次のコードを入力して、この接続文字列を作成します。 別のサーバー名または別のデータベース エンジンを使用している場合は、接続文字列の変更が必要になる場合があります。

    Private Function ConnectStringBuild() As String
        Dim strConn As String
    
        strConn &= "Data Source=(local);"
        strConn &= "Initial Catalog=Northwind;"
        strConn &= "User ID=sa"
    
        Return strConn
    End Function
    
  3. F5 押して、このアプリケーションを実行します。

すべてを正しく入力すると、リスト ボックスに製品の一覧が表示されます。

コードに関する問題

上記のコードに関する問題の 1 つは、テーブルに製品名が重複している可能性があるということです。 この場合、主キーをこのリスト ボックスに格納するメカニズムがないため、リスト内のレコードを一意に識別できます。 Microsoft Visual Basic® 6.0 とは異なり、.NET の ListBox には ItemData プロパティがありません。 ItemData は、テキスト ボックス内のテキストに関連付けられた長整数データ型を格納するために使用されました。 .NET によって ItemData プロパティが削除されました。 ただし、この種の状況を処理するには、ListBox 自体にオブジェクトを格納する方法がはるかに優れています。

ListBox にオブジェクトを格納する

接続文字列コードを機能させるには、リスト ボックスに配置するデータを保持するのに十分なプロパティを持つクラスを作成します。 このクラスの 1 つのプロパティは、リスト ボックスのテキスト部分にデータを表示するために使用されます。 他のプロパティ (プロパティ) は、主キー情報を保持するために使用されます。 Product テーブルの場合、ProductName 列には 1 つのプロパティ、ProductID 列には 1 つのプロパティが必要です。 実際、多くのテーブルでは、次のような 2 つのプロパティが必要になります。

**ヒント**説明と主キー値を持つ任意のリスト ボックスまたはコンボ ボックスの読み込みに再利用できる Value と ID という 2 つのプロパティを持つジェネリック クラスを作成します。

汎用 ListItem クラスを作成する

新しいファイルをプロジェクトに追加しましょう。 この新しいファイルには、汎用リスト アイテム クラスのクラス定義が含まれています。

  1. [プロジェクト] メニューの [クラス 追加... をクリック、新しいクラスをプロジェクトに追加します。

  2. clsListItems.vbなどの名前を付けます。

  3. 次のコードに示すようなクラスを作成します。

    Public Class PDSAListItemNumeric
        Private mstrValue As String
        Private mintID As Integer
    
        Public Sub New()
    
        End Sub
    
        Public Sub New(ByVal strValue As String, _
         ByVal intID As Integer)
            mstrValue = strValue
            mintID = intID
        End Sub
    
        Property Value() As String
            Get
                Return mstrValue
            End Get
            Set(ByVal Value As String)
                mstrValue = Value
            End Set
        End Property
    
        Property ID() As Integer
            Get
                Return mintID
            End Get
            Set(ByVal Value As Integer)
                mintID = Value
            End Set
        End Property
    
        Public Overrides Function ToString() As String
            Return mstrValue
        End Function
    End Class
    

上記のクラスには、リスト ボックスに配置する任意のテーブルのテキストと主キーを保持するために使用される Value と ID という 2 つのプロパティがあります。 リスト コントロールの Items コレクションに配置するクラスの場合は、ToString メソッドをオーバーライドする必要があります。 ToString は、すべてのクラスが自動的に継承する既定の Object データ型のメソッドです。 リスト ボックスに Items コレクション内の項目が表示されると、常に ToString メソッドを呼び出してデータを取得します。 このクラスの ToString メソッドでは、Value プロパティを返します。これは、リスト ボックスに表示する値です。

ListLoad で新しいクラスを使用する

この新しいジェネリック クラスを作成したので、ListLoad プロシージャで使用してみましょう。 今回は、DataReader から ProductName を直接追加する代わりに、ProductName と ProductID を PDSAListItemNumeric クラスの新しいインスタンスに追加し、このオブジェクトをリスト ボックスに追加します。

  1. 前に作成した ListLoad プロシージャのコードを、次のコードのように変更します。

    Private Sub ListLoad()
        Dim oCmd As SqlClient.SqlCommand
        Dim oDR As SqlClient.SqlDataReader
        Dim oItem As PDSAListItemNumeric
        Dim strSQL As String
        Dim strConn As String
    
        strConn = ConnectStringBuild()
    
        strSQL = "SELECT ProductID, ProductName "
        strSQL &= "FROM Products"
    
        Try
            oCmd = New SqlClient.SqlCommand()
            With oCmd
                .Connection = _
                 New SqlClient.SqlConnection(strConn)
                .Connection.Open()
                .CommandText = strSQL
                oDR = _
                 .ExecuteReader()
            End With
    
            lstProducts.Items.Clear()
            Do While oDR.Read()
                oItem = New PDSAListItemNumeric()
                With oDR
                    oItem.ID = CInt(.Item("ProductID"))
                    oItem.Value = _
                     .Item("ProductName").ToString()
                End With
    
                lstProducts.Items.Add(oItem)
            Loop
            If lstProducts.Items.Count > 0 Then
                lstProducts.SetSelected(0, True)
            End If
    
        Catch oExcept As Exception
            MessageBox.Show(oExcept.Message)
    
        End Try
    End Sub
    

このルーチンに対して行った主な変更は、SELECT ステートメントに ProductID 列を追加し、読み取りループ内に新しい oItem オブジェクトのインスタンス化を追加したということです。 新しいレコードを読み取るたびに、新しい PDSAListItemNumeric オブジェクトを作成し、ProductID を ID プロパティに格納し、ProductName を Value プロパティに格納します。 次に、この新しいオブジェクトをリスト ボックスに追加します。

試してみる

PDSAListItemNumeric クラスを使用するようにこのコードを変更したので、プロジェクトを実行して、すべてを正しく入力したことを確認します。 F5 押して、まだ製品がリスト ボックスに読み込まれているかどうかを確認します。

製品情報の表示

これらのオブジェクトをリスト ボックスに配置したら、リスト ボックス内の項目をクリックしてそのオブジェクトを取得し、ID プロパティと Value プロパティを取得できます。 リスト ボックス内の項目をクリックすると、SelectedIndexChanged イベント プロシージャが起動します。 このイベント内で、FormShow と呼ばれる次に記述する別のサブルーチンを呼び出すコードを記述できます。

  1. デザイン ビュー モードで [製品] フォームを表示します。

  2. リスト ボックスをダブルクリックして SelectedIndexChanged イベント プロシージャを表示します。

  3. FormShow という名前のルーチンに呼び出しを追加します。

    Private Sub lstProducts_SelectedIndexChanged( _
     ByVal sender As Object, ByVal e As System.EventArgs) _
     Handles lstProducts.SelectedIndexChanged
        FormShow()
    End Sub
    

次に、上記のイベント プロシージャの End Sub ステートメントの後に、この FormShow プロシージャを作成します。 FormShow は、リスト ボックスから PDSAListItemNumeric オブジェクトを取得し、選択した特定の項目の Products テーブルからすべての列を取得する SELECT ステートメントを作成します。 次に、その 1 行の DataReader オブジェクトを作成し、データ ソースからデータを読み取り、その製品のすべての詳細情報をこのフォームの適切なテキスト ボックスに配置します。

  1. このフォーム内に FormShow プロシージャを作成します。

  2. 次に示すコードを入力します。

    Private Sub FormShow()
        Dim oCmd As SqlClient.SqlCommand
        Dim oDR As SqlClient.SqlDataReader
        Dim oItem As PDSAListItemNumeric
        Dim strSQL As String
        Dim strConn As String
    
        strConn = ConnectStringBuild()
    
        ' Get Primary Key From List Box
        oItem = CType(lstProducts.SelectedItem, _
         PDSAListItemNumeric)
    
        strSQL = "SELECT ProductID, ProductName, "
        strSQL &= " QuantityPerUnit, UnitPrice, "
        strSQL &= " UnitsInStock, UnitsOnOrder, "
        strSQL &= " ReorderLevel, Discontinued "
        strSQL &= " FROM Products "
        strSQL &= " WHERE ProductID = " & oItem.ID
    
        Try
            oCmd = New SqlClient.SqlCommand()
            With oCmd
                .Connection = _
                 New SqlClient.SqlConnection(strConn)
                .Connection.Open()
                .CommandText = strSQL
                oDR = .ExecuteReader()
            End With
    
            If oDR.Read() Then
                With oDR
                    txtID.Text = .Item("ProductID").ToString()
                    txtName.Text = _
                     .Item("ProductName").ToString()
                    txtQty.Text = _
                     .Item("QuantityPerUnit").ToString()
                    txtPrice.Text = _
                     .Item("UnitPrice").ToString()
                    txtInStock.Text = _
                     .Item("UnitsInStock").ToString()
                    txtOnOrder.Text = _
                     .Item("UnitsOnOrder").ToString()
                    txtReorder.Text = _
                     .Item("ReorderLevel").ToString()
                    chkDisc.Checked = _
                     CType(.Item("Discontinued"), Boolean)
                End With
            End If
            oDR.Close()
            oCmd.Connection.Close()
    
        Catch oException As Exception
            MessageBox.Show(oException.Message)
    
        End Try
    End Sub
    

ListLoad プロシージャで作成したのと同じ種類のコーディングを使用する場合、FormShow プロシージャにはあまり新しい情報はありません。 リスト ボックスの SelectedItem プロパティから PDSAListItemNumeric オブジェクトを取得し、変数 oItem に配置します。

oItem = CType(lstProducts.SelectedItem, _
 PDSAListItemNumeric)

CType 関数は、あるデータ型を別のデータ型に変換します。 この場合、オブジェクト型を PDSAListItemNumeric 型に変換します。 リスト ボックスの Items コレクションに配置されたすべての項目は、汎用オブジェクト型として格納されます。

プロシージャの残りの部分では、Command オブジェクトを作成し、接続を開き、データ ソースから 1 つのレコードを読み取る DataReader オブジェクトを作成します。 その後、各列を受け取り、適切なテキスト ボックスにデータを配置します。

試してみる

FormShow プロシージャを作成したら、それを試して動作することを確認します。

  1. F5キー 押して、アプリケーションを起動します。
  2. リスト ボックスのエントリをクリックして、その特定の製品の詳細情報がフォームのテキスト ボックスに表示されることを確認します。

サプライヤーとカテゴリのコンボボックスについて心配しないでください。これらのコントロールを操作する方法については、次のセクションで説明します。

コンボ ボックスの読み込み

[製品] フォームには、カテゴリと仕入先という 2 つのコンボ ボックスを読み込む必要があります。 リスト ボックスを読み込むのと同じように、両方を読み込むことができます。 PDSAListItemNumeric クラスを使用して、コンボ ボックスの主キーとテキストの両方を読み込みます。 Categories テーブルで、CategoryID 列と CategoryName 列を選択します。 [仕入先] テーブルで、SupplierID 列と CompanyName 列を選択します。 Products テーブルの CategoryID 列と SupplierID 列は、それぞれ Categories テーブルと Suppliers テーブルの外部キーです。

  1. このフォーム内に CategoryLoad プロシージャを作成します。

  2. フォームのコード ウィンドウで、次に示すコードを入力します。

    Private Sub CategoryLoad()
        Dim oCmd As SqlClient.SqlCommand
        Dim oDR As SqlClient.SqlDataReader
        Dim strSQL As String
        Dim strConn As String
        Dim oItem As PDSAListItemNumeric
    
        strConn = ConnectStringBuild()
    
        strSQL = "SELECT CategoryID, CategoryName "
        strSQL &= "FROM Categories"
    
        Try
            oCmd = New SqlClient.SqlCommand()
            With oCmd
                .Connection = _
                 New SqlClient.SqlConnection(strConn)
                .Connection.Open()
                .CommandText = strSQL
                ' Closes connection when 
                ' closing DataReader object
               oDR = .ExecuteReader( _
                CommandBehavior.CloseConnection)
            End With
    
            Do While oDR.Read()
                oItem = New PDSAListItemNumeric()
                With oDR
                    oItem.ID = CInt(.Item("CategoryID"))
                    oItem.Value = _
                     .Item("CategoryName").ToString()
                End With
    
                cboCategory.Items.Add(oItem)
            Loop
            oDR.Close()
            ' No need to close this because of the 
            '.CloseConnection on the ExecuteReader
            'oCmd.Connection.Close()
    
        Catch oExcept As Exception
            MessageBox.Show(oExcept.Message)
    
        End Try
    End Sub
    

このルーチンと前に作成した ListLoad プロシージャの違いは 1 つあります。ExecuteReader メソッドでは、新しい定数 CloseConnection を渡しました。 このパラメーターは、リストの読み込みが完了したときに接続を閉じるよう DataReader クラスに指示します。 つまり、コマンド オブジェクトの Connection プロパティで Close メソッドを明示的に呼び出す必要はありません。

SupplierLoad プロシージャを作成する

CategoryLoad プロシージャと同様に、SupplierLoad プロシージャを作成します。 実際、CategoryLoad プロシージャをコピーしてフォームに貼り付け、名前を変更するだけです。

  1. CategoryLoad プロシージャをクリップボードにコピーし、そのコピーを CategoryLoad プロシージャのすぐ下に貼り付けます。
  2. 2 番目のプロシージャの名前を SupplierLoad に変更します。
  3. SELECT ステートメントで適切な列名とテーブル名を変更します。 使用する Suppliers テーブルの列は、SupplierID と CompanyName です。
  4. cboSupplier コンボ ボックスを使用するように、このプロシージャの cboCategory コンボ ボックスへのすべての参照を変更します。

フォームの読み込み時にこれらのプロシージャを呼び出す

frmProducts_Load イベント プロシージャからこれらのプロシージャの呼び出しを追加する必要があります。

  • frmProduct_Load イベント プロシージャを次のように変更します。

    Private Sub frmProduct_Load( _
     ByVal sender As System.Object, _
     ByVal e As System.EventArgs) Handles MyBase.Load
        ' Load Suppliers
        SupplierLoad()
        ' Load Categories
        CategoryLoad()
        ' Load List Box of Products
        ListLoad()
    End Sub
    

試してみる

プログラムを実行して、これらのルーチンが実際に画面上の適切なコンボ ボックスにデータを読み込んでいるかどうかを確認します。 F5 押して、カテゴリと仕入先のコンボ ボックスが読み込まれたかどうかを確認します。

コンボ ボックスでの値の検索

これで、カテゴリと仕入先のコンボ ボックスが読み込まれたので、リスト ボックスで新しい製品をクリックするたびに、コンボ ボックスを適切な値に配置できるようにする必要があります。 たとえば、Products テーブルの CategoryID 値が FormShow プロシージャの DataReader オブジェクトを介して読み込まれる場合、Category コンボ ボックスに読み込まれるオブジェクト内でその CategoryID を見つける必要があります。 試してみましょう。

  1. FormShow プロシージャを変更し、strID という名前の変数を使用して、ルーチンの先頭に Dim ステートメントを追加します。

    Dim strID As String
    
  2. FormShow プロシージャを変更し、CategoryID 列と SupplierID 列を SELECT ステートメントに追加します。

  3. 次に示すコードのセクションを見つけて、太字で強調表示されている行を追加します。

    strSQL = "SELECT ProductID, ProductName, "
    strSQL &= " SupplierID, CategoryID, "
    strSQL &= " QuantityPerUnit, UnitPrice, "
    strSQL &= " UnitsInStock, UnitsOnOrder, "
    strSQL &= " ReorderLevel, Discontinued "
    strSQL &= " FROM Products "
    strSQL &= " WHERE ProductID = " & oItem.ID
    
  4. DataReader からテキスト ボックスにデータを読み込む FormShow のコードセクションを見つけ、太字の次の行を追加します。

    txtID.Text = .Item("ProductID").ToString()
    txtName.Text = .Item("ProductName").ToString()
    strID = .Item("SupplierID").ToString()Call FindItem(cboSupplier, strID)strID = .Item("CategoryID").ToString()
    Call FindItem(cboCategory, strID)
    txtQty.Text = .Item("QuantityPerUnit").ToString()
    txtPrice.Text = .Item("UnitPrice").ToString()
    txtInStock.Text = .Item("UnitsInStock").ToString()
    txtOnOrder.Text = .Item("UnitsOnOrder").ToString()
    txtReorder.Text = .Item("ReorderLevel").ToString()
    
  5. 次に、コンボ ボックス参照と文字列変数を受け入れる FindItem 関数を作成します。 この関数は、コンボ ボックス内のオブジェクトの ID プロパティ内でその文字列変数を検索し、その位置にあるデータを表示するようにコンボ ボックスを設定します。

    Private Sub FindItem(ByVal cboCombo As ComboBox, _
     ByVal strID As String)
        Dim intLoop As Integer
        Dim boolFound As Boolean
        Dim oItem As PDSAListItemNumeric
    
        oItem = New PDSAListItemNumeric()
        For intLoop = 0 To cboCombo.Items.Count - 1
            oItem = CType(cboCombo.Items(intLoop), _
             PDSAListItemNumeric)
            If oItem.ID = CInt(strID) Then
                cboCombo.SelectedIndex = intLoop
                boolFound = True
                Exit For
            End If
        Next
        If Not boolFound Then
            cboCombo.SelectedIndex = -1
        End If
    End Sub
    

この FindItem 関数は、コンボ ボックス内のすべての値をループ処理し、毎回コンボ ボックスから項目を削除し、PDSAListItemNumeric オブジェクトに変換します。 返されたオブジェクトの ID プロパティを比較して、この関数に 2 番目のパラメーターとして渡される値と等しいかどうかを確認できます。 この値が見つかると、SelectedIndex プロパティがこの場所に設定され、コンボ ボックスがその値に強制的に配置されます。

パフォーマンスの問題

Command オブジェクトの ExecuteReader メソッドに別の CommandBehavior 定数を渡すことで、特定の状況のパフォーマンスをさらに向上させることができます。 主キー情報のみを読み取る KeyInfo*,* を指定できます。 SchemaOnly を指定して、データがアタッチされていない列スキーマ情報だけを読み取ることができます。 集計値のみを返す場合は、SingleColumn を指定できます。 または、1 つの行のみが返されることがわかっている場合は、SingleRow を指定して、この状況でパフォーマンスをさらに向上させます。

データの変更

また、データを追加、編集、および削除するアプリケーションのこのクライアント/サーバーの種類のプロシージャを作成する必要もあります。 このオブジェクトの ExecuteNonQuery メソッドを使用して、これらの INSERT、UPDATE、DELETE ステートメントを送信するには、Command オブジェクトを使用します。 例として、この画面のデータを更新するために書き込むコードを次に示します。

Private Sub DataUpdate()
    Dim oCmd As SqlClient.SqlCommand
    Dim strSQL As String
    Dim intRows As Integer

    strSQL = "UPDATE Products SET "
    strSQL &= "ProductName = " & _
     Str2Field(txtName.Text) & ", "
    strSQL &= "SupplierID = " & _
     CType(cboSupplier.Items(cboSupplier.SelectedIndex), _
     PDSAListItemNumeric).ID & ", "
    strSQL &= "CategoryID = " & _
     CType(cboCategory.Items(cboCategory.SelectedIndex), _
      PDSAListItemNumeric).ID & ", "
    strSQL &= "QuantityPerUnit = " & _
     Str2Field(cboSupplier.Text) & ", "
    strSQL &= "UnitPrice = " & txtPrice.Text & ", "
    strSQL &= "UnitsInStock = " & txtInStock.Text & ", "
    strSQL &= "UnitsOnOrder = " & txtOnOrder.Text & ", "
    strSQL &= "ReorderLevel = " & txtReorder.Text & ", "
    strSQL &= "Discontinued = " & _
     CType(IIf(chkDisc.Checked, "1", "0"), String)
    strSQL &= " WHERE ProductID =  " & _
     CType(lstProducts.SelectedItem, _
     PDSAListItemNumeric).ID

    Try
        oCmd = New SqlClient.SqlCommand()
        With oCmd
            .Connection = New _
             SqlClient.SqlConnection(ConnectStringBuild())
            .Connection.Open()
            .CommandText = strSQL
            intRows = .ExecuteNonQuery()
            If intRows <> 1 Then
                MessageBox.Show("Did not insert row")
            End If
            .Connection.Close()
        End With

    Catch oException As Exception
        MessageBox.Show(oException.Message)

    End Try
End Sub

上記のコードでは、標準の Command オブジェクトを使用して UPDATE SQL ステートメントを送信します。 参照先の Str2Field 関数は、文字列値を囲む単一引用符を追加します。 String2Field 関数を次に示します。

Private Function Str2Field(ByVal strValue As String) _
 As String
    If strValue.Trim() = "" Then
        Return "Null"
    Else
        Return "'" & strValue.Trim() & "'"
    End If
End Function

ADO.NET DataTable と DataSet の使用

ADO.NET DataSet オブジェクトは、メモリ内データベースに似ています。 このオブジェクトは、DataTable オブジェクトのコレクションを保持します。 各 DataTable オブジェクトは、SELECT ステートメントまたはストアド プロシージャの実行を介して取得されたデータの表現です。 DataSet 内のデータは、XML として書き込んだり読み込んだりできます。 DataSet には、複数の DataTable オブジェクト間のスキーマ情報、制約、リレーションシップも格納されます。 DataSet を使用すると、データを追加、編集、および削除できます。

データセットは、特に、フォーム上のコントロールに表示されるデータ ソースからデータを取得するために使用できます。 このセクションでは、DataTable オブジェクトと DataSet オブジェクトを使用してデータ入力フォームを作成する方法について説明します。 図 3 は、このドキュメントで前に作成したのと基本的に同じフォームでビルドするサンプル画面を示しています。 Northwind データベースの Products テーブルを使用します。 このサンプル データベースは Access データベースとして提供され、SQL Server の一部としてインストールされます。

3: DataSets を使用した画面の追加/編集/削除

このフォームを作成するには、次の手順に従って、前に作成したフォームをコピーします。

  1. ソリューション エクスプローラーで、frmProducts.vb フォームをクリックします。
  2. Ctrl + C 押して、フォームをクリップボードにコピーします。
  3. Ctrl + V 押して、このフォームのコピーをソリューション エクスプローラーに貼り付けます。
  4. フォームの名前を frmProductsDS.vb に変更します。
  5. フォームのコード ウィンドウを開き、パブリック クラス Form1 読み取る行を、frmProductsDSパブリック クラス に変更します。
  6. フォームをコピーすると、作成したすべての手順もコピーされます。 このコードをそのまま使用することも、すべてのプロシージャを削除することもできます。

DataReader を使用して DataTable と DataSet を使用して、前に作成したルーチンを再構築するために使用できる新しいフォームが作成されました。

DataTable オブジェクトを使用した ComboBox の読み込み

製品情報フォームには、2 つのコンボ ボックスがあります。 1 つは製品のカテゴリ用で、もう 1 つは製品のサプライヤー用です。 DataTable オブジェクトを使用して、これらのコンボ ボックスにデータを読み込みます。

DataTable オブジェクトを使用して仕入先コンボ ボックスを読み込むには、次の手順に従います。

  1. frmProductDS.vb フォームをクリックし、[コードの表示] アイコンをクリックします (または、[表示] メニューの [コード] をクリックします)。

  2. Windows フォーム デザイナーで生成されたコード行のすぐ下に SupplierLoad プロシージャを作成します。 次に示すコードをこの新しい手順に追加します。

    Private Sub SupplierLoad()
        Dim oAdapter As SqlClient.SqlDataAdapter
        Dim oTable As DataTable = New DataTable()
        Dim oItem As PDSAListItemNumeric
        Dim strSQL As String
        Dim strConn As String
        Dim intLoop As Integer
    
        strConn = ConnectStringBuild()
    
        strSQL = "SELECT SupplierID, CompanyName "
        strSQL &= "FROM Suppliers"
    
        Try
            oAdapter = _
             New SqlClient.SqlDataAdapter(strSQL, strConn)
            oAdapter.Fill(oTable)
    
            For intLoop = 0 To oTable.Rows.Count - 1
                oItem = New PDSAListItemNumeric()
                With oTable.Rows(intLoop)
                    oItem.Value = _
                     .Item("CompanyName").ToString()
                    oItem.ID = CInt(.Item("SupplierID"))
                End With
    
                cboSupplier.Items.Add(oItem)
            Next
    
        Catch oExcept As Exception
            MessageBox.Show(oExcept.Message)
    
        End Try
    End Sub
    

このコンボ ボックスを読み込むには、3 つのオブジェクトが必要になります。 DataAdapter が必要です。これは、DataTable または DataSet にデータを格納するために使用されるオブジェクトです。 データ ソースに送信された SELECT ステートメントによって取得されたすべてのデータを保持する DataTable オブジェクトが必要です。 また、PDSAListItemNumeric クラスが必要です。このクラスには、コンボ ボックスに表示する値と、Suppliers テーブルの主キー値を配置します。 PDSAListItemNumeric クラスは既にビルドされており、このドキュメントの先頭に読み込んだソリューション内に含まれています。

接続文字列の構築

ADO.NET を使用して SQL ステートメントをデータ ソースに送信する前に、このデータ ソースが存在する場所と、このデータを取得するために使用するプロバイダーに関する ADO.NET 指示を与える必要があります。 これは接続文字列を使用して行われます。 作成した SupplierLoad プロシージャで、このプロバイダー文字列を返す ConnectStringBuild という関数を呼び出しました。 この関数を作成しましょう。

作成した SupplierLoad プロシージャの End Sub のすぐ下に、次の関数を追加します。

Private Function ConnectStringBuild() As String
    Dim strConn As String

    strConn &= "Data Source=(local);"
    strConn &= "Initial Catalog=Northwind;"
    strConn &= "User ID=sa"

    Return strConn
End Function

ローカル コンピューターではなく、ネットワーク上のどこかにある SQL Server を使用している場合は、この接続文字列の変更が必要になる場合があります。 SQL Server をお持ちでない場合は、Microsoft Access に付属のNorthwind.mdb ファイルをサンプル データベースとして使用できます。 Access を使用している場合は、System.Data.OleDb 名前空間にある OleDbConnection、OleDbCommand、および OleDbDataAdapter オブジェクトを使用し、接続文字列を次のように変更する必要があります。

Provider=Microsoft.Jet.OleDb.4.0;Data Source=C:\Access\Northwind.mdb

データ ソース属性のパスを、Northwind.mdbが配置されているパスに変更します。

データ アダプターを使用して DataTable にデータを入力する

接続文字列と SQL 文字列を作成したら、DataAdapter オブジェクトの新しいインスタンスを作成し、それに SQL 文字列と接続文字列を渡します。 DataAdapter で Fill メソッドを呼び出し、DataTable オブジェクトに渡します。 Fill メソッドは、指定された接続文字列を使用してデータベースへの接続を開き、DataTable にデータを入力してから、接続を閉じます。

DataTable にデータを読み込んだら、DataTable を構成する各 DataRow オブジェクトをループ処理できます。 ループを実行するたびに、新しい PDSAListItemNumeric オブジェクトを作成し、ID プロパティに主キー (SupplierID) を追加し、CompanyName 列を Value プロパティに追加します。 次に、この PDSAListItemNumeric オブジェクトを ListBox に追加します。

Visual Basic 6.0 のように、リスト ボックス コントロールに ItemData プロパティがないため、前に作成した汎用 PDSAListItemNumeric クラスを使用して、主キー データと表示するテキスト値を保持する必要があります。

[カテゴリ] コンボ ボックスを読み込む

この製品のフォームのコンボ ボックスにカテゴリを読み込むには、SupplierLoad プロシージャをコピーして貼り付け、SELECT ステートメントの名前、列、およびテーブル名を変更します。

  1. コードを強調表示し、Ctrl + C押して、完全な SupplierLoad プロシージャをメモリにコピーします。
  2. カーソルを置き、Ctrl + Vキーを押して、SupplierLoad プロシージャの End Sub のすぐ下にコード 貼り付けます。
  3. この新しいプロシージャの名前を CategoryLoad に変更します。
  4. 列 CategoryID と CategoryName を使用するように SELECT ステートメントを変更します。
  5. テーブル名 Categories を使用するように SELECT ステートメントを変更します。
  6. PDSAListItemNumeric クラスを読み込む列を CategoryID と CategoryName に変更します。

試してみる

DataTable オブジェクトを使用して Suppliers と Categories を読み込むためのすべてのコードを入力したので、それらが機能するかどうかを見てみましょう。

  1. フォームを開いて、フォームがデザイン モードで表示されるようにします。

  2. フォーム自体の任意の場所 (コントロールではなく) をダブルクリックし、frmProduct_Load イベント プロシージャ内にコードを記述して、先ほど記述した各ルーチンを呼び出します。

    Private Sub frmProduct_Load( _ 
     ByVal sender As System.Object, _
     ByVal e As System.EventArgs) Handles MyBase.Load
        ' Load Suppliers
        SupplierLoad()
        ' Load Categories
        CategoryLoad()
    End Sub
    
  3. 次に、F5キー 押してプログラムを実行します。

問題がなければ、空白の製品フォームが表示されますが、[仕入先] コンボ ボックスと [カテゴリ] コンボ ボックスにはデータが含まれている必要があります。

DataSet オブジェクトの作成

DataTable オブジェクトは、1 つのテーブルのデータ量を保持し、コンボ ボックスとリスト ボックスを読み込む場合に便利です。 DataSet オブジェクトは、1 つ以上の DataTable オブジェクトのラッパーのようなものです。 DataSet オブジェクトを使用する方法について説明します。

データ入力画面をまとめる場合は、Form クラスのプロパティとして宣言する DataSet を 1 つ作成すると便利です。 その後、このプロパティをフォーム全体で使用して、リスト ボックスにデータを読み込み、フォームのデータを追加、編集、削除できます。

  1. サンプル プロジェクトでfrmProducts.vb ファイルを開きます。

  2. コード ウィンドウを開き、フォーム コードの一番上に移動します。

  3. Inherits ステートメントの直後に、DataSet クラスとして宣言 moDS という Private 変数を追加します (次のコードの太字のテキストを参照)。

    Public Class frmProduct
        Inherits System.Windows.Forms.Form
    
        Private moDS As DataSet
    

    これにより、フォーム全体で使用できるプライベート変数が作成されます。 次に、DataSet を作成し、データと共に読み込みます。

  4. DataSetCreate という名前の新しいプロシージャを作成し、次に示すコードをこの新しいプロシージャに入力します。

    Private Sub DataSetCreate()
        Dim oAdapter As SqlClient.SqlDataAdapter
        Dim strSQL As String
        Dim strConn As String
    
        ' Get Connection String
        strConn = ConnectStringBuild()
    
        ' Build SQL String
        strSQL = "SELECT * "
        strSQL &= "FROM Products"
    
        moDS = New DataSet()
        Try
            ' Create New Data Adapter
            oAdapter = _
             New SqlClient.SqlDataAdapter(strSQL, strConn)
            ' Fill DataSet From Adapter and give it a name
            oAdapter.Fill(moDS , "Products")
            ' Create a Primary Key
            With moDS.Tables("Products")
                 .PrimaryKey = New DataColumn() _
                  {.Columns("ProductID")}
            End With
    
        Catch oExcept As Exception
            MessageBox.Show(oExcept.Message)
    
        End Try
    End Sub
    

上記のコードのほとんどは、今では使い慣れているはずです。 接続文字列と SQL SELECT ステートメントを作成します。 DataTable の場合と同様に、DataSet を読み込む DataAdapter も作成します。 そこから、物事は少し異なります。

Fill メソッドは引き続き使用しますが、DataSet オブジェクトと、この DataSet 内に作成された新しい DataTable に関連付ける名前を渡します。 上記の場合は、この新しい DataTable に製品名前を付けます。 その後、この名前を使用して、使用する DataSet の Tables コレクション内のテーブルを参照できます。

Tables コレクションを使用して、個々の DataTable を参照できます。 たとえば、moDS.Tables("Products") は、DataAdapter で Fill メソッドを実行したときに作成された DataTable オブジェクトを返します。 Fill メソッドの 2 番目のパラメーターとして "Products" を渡したので、この名前がこの DataTable に割り当てられたことに注意してください。 この手法を使用して、どの列が主キーであるかを DataTable に伝えることができます。

DataSet でこの DataTable 内で検索を行う予定がある場合は、PrimaryKey 列である DataTable を指定する必要があります。 これを行うには、PrimaryKey プロパティを New DataColumn 配列に設定します。 テーブルには主キーとして 1 つ以上の列を含めることができるため、この PrimaryKey プロパティに渡す DataColumn オブジェクトの配列を作成する必要があります。 上記のコードで行ったことは、 .Columns("ProductID") 列は、新しい DataColumn 配列を宣言した直後 {} 中かっこで囲みます。 中かっこは、この新しい配列に入りたい要素を宣言する方法です。 複数の列がある場合は、各列を中かっこ内のコンマで区切ります。

DataSet からのリスト ボックスの読み込み

製品データのデータセットを作成し、moDSという名前 変数に格納しました。 この変数は、このフォーム内の任意の場所で使用できます。 次に、DataSet からこのフォームのリスト ボックスに製品データを読み込むルーチンを作成します。

  1. フォーム内のどこかに ListLoad という名前の新しいプロシージャを作成します。

  2. このプロシージャに次のコードを入力します。

    Private Sub ListLoad()
        Dim oItem As PDSAListItemNumeric
        Dim oRow As DataRow
    
        LstProducts.Items.Clear()
        ' Loop through each row and get a DataRow
        For Each oRow In moDS.Tables("Products").Rows
            ' Create New Item to hold PK and Description
            oItem = New PDSAListItemNumeric()
            With oItem
                .ID = CInt(oRow.Item("ProductID"))
                .Value = oRow.Item("ProductName").ToString()
            End With
    
            ' Add Item to list box
            lstProducts.Items.Add(oItem)
        Next
    
        lstProducts.SetSelected(0, True)
    End Sub
    

ListLoad プロシージャのコードは非常に単純です。 DataSet 内の各 DataTable は、DataRow オブジェクトで構成されます。 DataTable 内の各 DataRow をループ処理し、ループを通じて毎回、PDSAListItemNumeric オブジェクトをビルドして ProductID と ProductName を配置できます。 次に、この新しいオブジェクトをリスト ボックスに追加します。

試してみる

これで、入力したコードを実行し、製品名でいっぱいのリスト ボックスを読み込むことができるようになります。

  1. frmProduct_Load イベント プロシージャを変更して、作成したこれら 2 つの新しいルーチンを呼び出します。

  2. 次に示すコード行を太字で追加します。

    Private Sub frmProduct_Load( _ 
     ByVal sender As System.Object, _
     ByVal e As System.EventArgs) Handles MyBase.Load
        ' Load Suppliers
        SupplierLoad()
        ' Load Categories
        CategoryLoad()
    
        ' Initialize the DataSet
        DataSetCreate()
        ' Load List Box of Products
        ListLoad()
    End Sub
    
  3. F5 押して、このプログラムを実行します。

フォームが表示されたら、製品名でいっぱいのリスト ボックスが表示されます。

DataSet での特定の行の検索

ユーザーがリスト ボックスで製品をクリックすると、リスト ボックスの右側にある適切なコントロールに各製品の詳細データが表示されます。 最初に行う必要があるのは、リスト ボックスをクリックしたユーザーに応答するコードを記述することです。

  1. デザイン モードで [製品] フォームを表示します。

  2. リスト ボックスをダブルクリックして SelectedIndexChanged イベント プロシージャを表示します。

  3. 次に示すように、FormShow という名前のプロシージャを呼び出すコードを追加します。

    Private Sub lstProducts_SelectedIndexChanged( _
     ByVal sender As Object, ByVal e As System.EventArgs) _
     Handles lstProducts.SelectedIndexChanged
        FormShow()
    End Sub
    
  4. DataSet 内の特定の行を検索し、その行から情報を取得し、Product フォームのすべての適切なコントロールを入力する FormShow プロシージャを記述します。

  5. Product フォームに新しいプロシージャを追加し、FormShow という名前を付けます。

  6. 次に示すコードを記述します。

    Private Sub FormShow()
        Dim oDR As DataRow
        Dim strID As String
    
        ' Get Primary Key From List Box
        strID = CType(lstProducts.SelectedItem, _
         PDSAListItemNumeric).ID.ToString()
    
        ' Find row in DataSet
        oDR = moDS.Tables("Products").Rows.Find(CInt(strID))
        txtID.Text = oDR("ProductID").ToString()
        txtName.Text = oDR("ProductName").ToString()
        strID = oDR("SupplierID").ToString()
        Call FindItem(cboSupplier, strID)
        strID = oDR("CategoryID").ToString()
        Call FindItem(cboCategory, strID)
        txtQty.Text = oDR("QuantityPerUnit").ToString()
        txtPrice.Text = oDR("UnitPrice").ToString()
        txtInStock.Text = oDR("UnitsInStock").ToString()
        txtOnOrder.Text = oDR("UnitsOnOrder").ToString()
        txtReorder.Text = oDR("ReorderLevel").ToString()
        chkDisc.Checked = CType(oDR("Discontinued"), Boolean)
    End Sub
    

FormShow プロシージャで最初に行うことは、リスト ボックスから SelectedItem を取得することです。 リスト ボックスの各項目は PDSAListItemNumeric オブジェクトです。 リスト ボックスには Object データ型のみが含まれているため、CType 関数を使用してオブジェクトを PDSAListItemNumeric データ型に変換する必要があります。 その後、このオブジェクトから ID プロパティを取得して、クリックした行の主キーを取得できます。

Rows の Find メソッドは、単一の DataRow オブジェクトを返します。 FormShow プロシージャでは、oDR という名前の変数を DataRow オブジェクトとして宣言しました。 ID プロパティを Find メソッドに渡して、見つかった DataRow への参照を返します。 この DataRow オブジェクトを取得すると、各列のデータを取得できます。

DataSet への行の追加

ある時点で、ユーザーがテーブルに行を追加できるようにする必要があります。 これを行うには、Command オブジェクトを使用して INSERT ステートメントを送信するか、既に作成した DataSet オブジェクトを使用します。 DataSet とデータベースに新しい行を追加するには、いくつかの手順を実行します。 DataSet はデータベースから切断されているため、最初に新しいデータを DataSet に追加する必要があります。 次に、データベースへの接続を構築し、INSERT ステートメントを作成する必要があります。 Microsoft は、CommandBuilder オブジェクトと呼ばれる、この INSERT ステートメントを作成するオブジェクトを提供しています。

  1. 製品フォームに新しいプロシージャを追加します。

  2. このプロシージャに DataAdd という名前を付けます。

  3. 次に示すコードを記述します。

    Private Sub DataAdd()
        Dim oAdapter As SqlClient.SqlDataAdapter
        Dim oBuild As SqlClient.SqlCommandBuilder
        Dim oDR As DataRow
        Dim strSQL As String
        Dim strConn As String
    
        ' Create New DataRow Object From DataSet
        oDR = moDS.Tables("Products").NewRow()
        oDR.BeginEdit()
    
        ' Load new data into row
        oDR("ProductName") = txtName.Text
        oDR("SupplierID") = CType(cboSupplier.SelectedItem, _
         PDSAListItemNumeric).ID
        oDR("CategoryID") = CType(cboCategory.SelectedItem, _
         PDSAListItemNumeric).ID
        oDR("QuantityPerUnit") = cboSupplier.Text
        oDR("UnitPrice") = CDec(txtPrice.Text)
        oDR("UnitsInStock") = CShort(txtInStock.Text)
        oDR("UnitsOnOrder") = CShort(txtOnOrder.Text)
        oDR("ReorderLevel") = CShort(txtReorder.Text)
        oDR("Discontinued") = CBool(chkDisc.Checked)
    
        ' Tell DataRow you are done adding data
        oDR.EndEdit()    
        ' Add DataRow to DataSet
        moDS.Tables("Products").Rows.Add(oDR)
    
        Try
            ' Get Connection String
            strConn = ConnectStringBuild()
            ' Build SQL String
            strSQL = "SELECT * FROM Products "
            ' Create New DataAdapter
            oAdapter = _
             New SqlClient.SqlDataAdapter(strSQL, strConn)
            ' Create CommandBuilder for Adapter
            ' This will build INSERT, UPDATE and DELETE SQL
            oBuild = New SqlClient.SqlCommandBuilder(oAdapter)
    
            ' Get Insert Command Object
            oAdapter.InsertCommand = oBuild.GetInsertCommand()
    
            ' Submit INSERT statement through Adapter
            oAdapter.Update(moDS, "Products")
            ' Tell DataSet changes to data source are complete
            moDS.AcceptChanges()
    
            ' Reload the list box
            ListLoad()
    
        Catch oException As Exception
            MessageBox.Show(oException.Message)
    
        End Try
    End Sub
    

データベース内のテーブルに新しいレコードを追加するには、最初に DataSet の DataTable に新しい行を追加する必要があります。 これを行うには、NewRow メソッドを使用して新しい DataRow を作成します。 この新しい DataRow で BeginEdit メソッドを呼び出して、適切な列にデータを配置できるようにします。 すべての列を更新したら、EndEdit メソッドを呼び出します。 DataTable の Rows コレクションの Add メソッドに DataRow を渡して、この新しい DataRow を DataSet の DataTable に追加します。

コマンド ビルダー オブジェクトを使用して SQL を作成する

DataSet にデータが格納されたので、DataAdapter を作成して、この新しいデータをデータベースに送信できます。 DataSet と接続文字列の読み込みに使用したのと同じ SQL ステートメントを渡して、DataAdapter を作成します。 その後、DataAdapter オブジェクトを CommandBuilder クラスのコンストラクターに渡すと、新しい Builder オブジェクトが作成されます。 その後、この CommandBuilder オブジェクトで GetInsertCommand メソッドを呼び出して、このテーブルの INSERT ステートメントを含むコマンド オブジェクトを取得できます。 INSERT ステートメントでは、DataSet 内の各データのプレースホルダーとして疑問符が使用されます。

SqlCommandBuilder を使用する場合、INSERT ステートメントは次のようになります。

INSERT INTO "Products"( "ProductName" , "SupplierID" , "CategoryID" , 
"QuantityPerUnit" , "UnitPrice" , "UnitsInStock" , "UnitsOnOrder" , 
"ReorderLevel" , "Discontinued" ) VALUES ( @ProductName , @SupplierID , 
@CategoryID , @QuantityPerUnit , @UnitPrice , @UnitsInStock , 
@UnitsOnOrder , @Reorderlevel , @Discontinued )

OleDbCommandBuilder を使用すると、INSERT ステートメントは次のようになります。

INSERT INTO "Products"( "ProductName" , "SupplierID" , "CategoryID" , 
"QuantityPerUnit" , "UnitPrice" , "UnitsInStock" , "UnitsOnOrder" , 
"ReorderLevel" , "Discontinued" ) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? 
, ? )

ステートメント内の各パラメーター マーカー (SqlCommand の場合は "@<columnname>"、OleDbCommand の場合は "?" は、DataAdapter を使用してこの INSERT ステートメントを送信するときに DataSet からのデータを配置する場所を表します。 この置換は DataAdapter によって自動的に行われ、追加のプログラミングは必要ありません。 DataAdapter にこの INSERT ステートメントを送信するように指示するには、DataSet オブジェクトと、DataAdapter の Update メソッドに更新される DataSet 内のテーブルの名前を渡します。 この Update メソッドが完了したら、DataSet で AcceptChanges メソッドを呼び出します。 これにより、データベースで更新されたことを新しい DataRow に通知します。

試してみる

このルーチンを作成して新しい行を追加したら、試してみる必要があります。

  1. デザイン モードでフォームを表示します。

  2. [追加] をダブルクリックします。

  3. btnAdd_Click イベント プロシージャで、DataAdd プロシージャを呼び出します。

    Private Sub btnAdd_Click(ByVal sender As Object, _
     ByVal e As System.EventArgs) Handles btnAdd.Click
        DataAdd()
    End Sub
    
  4. F5 押して、アプリケーションを実行します。

  5. [製品名] テキスト ボックスに 新しい製品 を入力します。

  6. [追加] をクリックします。 新しい製品がリスト ボックスに表示されます。

DataSet 内の行の更新

DataSet 内の行の更新は、行の追加とほぼ同じです。 新しい行を追加する代わりに、更新する既存の行が見つかります。 次に、DataRow 内の適切な列にデータを更新し、DataAdapter オブジェクトと CommandBuilder オブジェクトをビルドして、UPDATE コマンド オブジェクトを取得します。 その後、DataAdapter の Update メソッドを使用して UPDATE ステートメントを送信し、変更を受け入れて、完了します。

  1. このフォームの新しいプロシージャとして、次のコードを入力します。

    Private Sub DataUpdate()
        Dim oAdapter As SqlClient.SqlDataAdapter
        Dim oBuild As SqlClient.SqlCommandBuilder
        Dim oDR As DataRow
        Dim strSQL As String
        Dim strID As String
        Dim strConn As String
    
        ' Get Primary Key From List Box
        strID = CType(lstProducts.SelectedItem, _
         PDSAListItemNumeric).ID.ToString()
    
        ' Find Row To Update
        oDR = moDS.Tables("Products").Rows.Find(CInt(strID))
    
        ' Begin the editing process
        oDR.BeginEdit()
    
        ' Load new data into row
        oDR("ProductName") = txtName.Text
        oDR("SupplierID") = CType(cboSupplier.SelectedItem, _
         PDSAListItemNumeric).ID
        oDR("CategoryID") = CType(cboCategory.SelectedItem, _
         PDSAListItemNumeric).ID
        oDR("QuantityPerUnit") = cboSupplier.Text
        oDR("UnitPrice") = CDec(txtPrice.Text)
        oDR("UnitsInStock") = CShort(txtInStock.Text)
        oDR("UnitsOnOrder") = CShort(txtOnOrder.Text)
        oDR("ReorderLevel") = CShort(txtReorder.Text)
        oDR("Discontinued") = CBool(chkDisc.Checked)
    
        ' End the editing process
        oDR.EndEdit()
    
        Try
            ' Get Connection String
            strConn = ConnectStringBuild()
            ' Build SQL String
            strSQL = "SELECT * FROM Products "
            ' Create New DataAdapter
            oAdapter = New _
             SqlClient.SqlDataAdapter(strSQL, strConn)
            ' Create CommandBuild from Adapter
            ' This will build INSERT, UPDATE and DELETE SQL
            oBuild = New SqlClient.SqlCommandBuilder(oAdapter)
    
            ' Get Update Command Object
            oAdapter.UpdateCommand = oBuild.GetUpdateCommand()
    
            ' Submit UPDATE through Adapter
            oAdapter.Update(moDS, "Products")
            ' Tell DataSet changes to data source are complete
            moDS.AcceptChanges()
    
            ' Reload the list box
            ListLoad()
    
        Catch oException As Exception
            MessageBox.Show(oException.Message)
    
        End Try
    End Sub
    

ご覧のように、このコードは、前に記述した DataAdd プロシージャとほぼ同じです。 最大の違いは、GetInsertCommand メソッドを呼び出す代わりに、GetUpdateCommand を呼び出すことです。 GetUpdateCommand から取得したコマンド オブジェクトを DataAdapter の UpdateCommand プロパティに配置します。

試してみる

このルーチンを作成して新しい行を追加したら、試してみる必要があります。

  1. デザイン モードでフォームを表示します。

  2. 更新をダブルクリックします。

  3. btnUpdate_Click イベント プロシージャで、DataUpdate プロシージャを呼び出します。

    Private Sub btnUpdate_Click(ByVal sender As Object, _
     ByVal e As System.EventArgs) Handles btnUpdate.Click
        DataUpdate()
    End Sub
    
  4. F5 押して、アプリケーションを実行します。

  5. 製品名の 1 つを変更し、レコードの 1 つの他のフィールドの 2 つを変更します。

  6. [更新] をクリックします。

  7. 別の製品をクリックし、更新した製品をクリックします。 更新されたデータが製品フォームのコントロールに表示されます。

DataSet 内の行の削除

この時点までに、DataSet オブジェクトを介してデータを更新するためのパターンが表示されるはずです。 実際、DataSet からデータを削除するには、ほぼ同じコードを記述しますが、DataRow 内の適切なデータを更新する代わりに、代わりに Delete メソッドを適用します。 その後、作成した DataAdd プロシージャと DataUpdate プロシージャと同様に、DataAdapter を使用してこの変更を送信します。

  1. フォームに DataDelete という名前の新しいプロシージャを追加します。

  2. この新しいプロシージャに次のコードを記述します。

    Private Sub DataDelete()
        Dim oAdapter As SqlClient.SqlDataAdapter
        Dim oBuild As SqlClient.SqlCommandBuilder
        Dim oDR As DataRow
        Dim strSQL As String
        Dim strID As String
        Dim strConn As String
    
        ' Get Connection String
        strConn = ConnectStringBuild()
    
        ' Get Primary Key From List Box
        strID = CType(lstProducts.SelectedItem, _
         PDSAListItemNumeric).ID.ToString()
    
        ' Find DataRow To Delete
        oDR = moDS.Tables("Products").Rows.Find(CInt(strID))
        ' Mark DataRow for deletion
        oDR.Delete()
    
        Try
            ' Build SQL String
            strSQL = "SELECT * FROM Products "
            ' Create New DataAdapter
            oAdapter = New _
             SqlClient.SqlDataAdapter(strSQL, strConn)
            ' Create CommandBuild from Adapter
            ' This will build INSERT, UPDATE and DELETE SQL
            oBuild = New SqlClient.SqlCommandBuilder(oAdapter)
    
            ' Get Delete Command Object
            oAdapter.DeleteCommand = oBuild.GetDeleteCommand()
    
            ' Submit DELETE through Adapter
            oAdapter.Update(moDS, "Products")
            ' Tell DataSet changes to data source are complete
            moDS.AcceptChanges()
    
            ' Reload the list box
            ListLoad()
    
        Catch oException As Exception
            MessageBox.Show(oException.Message)
    
        End Try
    End Sub
    

上記のコードでは、削除する行を見つけて、その DataRow オブジェクトに Delete メソッドを適用します。 これにより、DataSet 内の削除対象の行がマークされます。 もう一度 CommandBuilder オブジェクトを使用して DeleteCommand オブジェクトを取得し、DataAdapter で Update メソッドを呼び出して、この DELETE ステートメントをデータベースに送信します。

試してみる

行を削除するためにこのルーチンを作成したら、試してみる必要があります。

  1. デザイン モードでフォームを表示します。

  2. [削除] ボタンをダブルクリックします。

  3. btnDelete_Click イベント プロシージャで、DataDelete プロシージャを呼び出します。

    Private Sub btnDelete_Click(ByVal sender As Object, _
     ByVal e As System.EventArgs) Handles btnUpdate.Click
        DataDelete()
    End Sub
    
  4. F5 押して、アプリケーションを実行します。

  5. リスト ボックスでいずれかの製品をクリックします。

  6. [削除] をクリックします。 これで、この製品がリスト ボックスから消えることがわかります。

Visual Basic 6.0 と ADO の違い

Visual Basic 6.0 では、データの操作に使用する主なオブジェクトは RecordSet オブジェクトです。 この RecordSet オブジェクトは、通常、Connection オブジェクトを作成することによって取得され、その接続を Command オブジェクトに関連付け、コマンドで Execute を呼び出します。 Command オブジェクトと RecordSet オブジェクトに設定されたプロパティ、およびデータベースの機能に応じて、結果の RecordSet の動作は非常に異なる場合があります。RecordSet は順方向専用であるか、スクロールをサポートしている可能性があり、行の正確な数を示すことができるか、できないか、更新できないか、データベースへの継続的な接続が必要な場合や、完全にメモリにキャッシュされる可能性があります。

ADO.NET では、Connection オブジェクトと Command オブジェクトは引き続き使用できますが、データの使用方法に応じて異なる種類のオブジェクトを使用します。

データを 1 回ループするだけで、読み取り中にデータに変更を加えようとしていない場合は、Connection で ExecuteReader メソッドを呼び出して DataReader を返すことができます。 DataReader は、データをデータベースから読み取るための最適なパフォーマンスを提供する、前方のみ読み取り専用の結果ストリームです。

データをスクロールして更新する場合は、DataAdapter で Fill メソッドを呼び出して、結果を DataSet に配置できます。 DataSet は、データのメモリ内キャッシュを提供します。これにより、結果間を移動し、切断されたメモリ内キャッシュ内のデータを更新できます。 その後、DataAdapter で Update メソッドを呼び出すことによって、変更がデータベースに送り返されます。

ADO.NET で機能を個別のコンポーネントに分離すると、開発者は.NET Framework 内でのデータへのアクセス、表現、および使用方法をより詳細に制御できます。

概要

このドキュメントでは、DataReader、DataTable、DataSet オブジェクトを使用してクライアント サーバー アプリケーションを作成する方法について説明します。 このドキュメントに示されている各データ アクセス方法は問題なく機能しますが、アプリケーションを作成するときに、最も好きな方法を選択し、一貫して使用することをお勧めします。

作成者について

Paul D. Sheriff は、南カリフォルニアのカスタム ソフトウェア開発およびコンサルティング会社である PDSA, Inc.の所有者です。 Paul は南カリフォルニアの MSDN 地域ディレクターであり、Visual Basic 6.0 の書籍の著者であり、Paul Sheriff 氏が Visual Basicについて教えています。また、Visual Basic、SQL Server、.NET、および Keystone Learning Systems の Web 開発に関する 72 以上のビデオを制作しています。 ポールはジャンプスタートASP.NET タイトルの本を共同編集しました. 詳細については、PDSA, Inc. Web サイト (www.pdsa.com) を参照してください。

情報伝達グループについて

インフォーマント・コミュニケーションズ・グループ(www.informant.com)は、情報技術分野を中心とした多様なメディア企業です。 ソフトウェア開発出版物、カンファレンス、カタログ発行、Webサイトを専門とするICGは、1990年に設立されました。 ICGは、米国および英国にオフィスを構え、質の高い技術情報に対するITプロフェッショナルの高い意欲を満たす、メディアおよびマーケティングコンテンツインテグレーターとして高い評価を得ています。

Copyright © 2002 Informant Communications Group and Microsoft Corporation

技術編集: PDSA, Inc.