DataGridコントロールに連結するだけですべての業務が済めば楽なのだが、実際にはデータセット内の個々の実データを読み書きする必要が出てくる。このため、データセットを構成するオブジェクト階層は熟知しておかなければならない。少々ややこしいが「データセットの基本構造」で示した図も参照しながら、以下の解説を読んでいただきたい。
■DataSetクラスのTablesプロパティ
今回は利用していないが、1つのデータセットには複数のテーブル(複数のselect文の実行結果)を格納することができる。つまり複数のDataTableオブジェクトへの参照を保持することができ、DataSetクラスのTablesプロパティによりそれにアクセスできる。
TablesプロパティにはDataTableCollectionクラスのオブジェクトがセットされている。このクラスでは、インデクサにより配列の要素にアクセスするように各DataTableオブジェクトを参照することができる。
例えば、データセットを参照するDataSet型の変数dsがあるとすると、各DataTableオブジェクトはTablesプロパティを経由してds.Tables[0]、ds.Tables[1]、……といったぐあいに参照できる。
■DataTableクラスのRowsプロパティ
DataTableとDataRowとの関係は、いま述べたDataSetとDataTableの関係とよく似ている。DataTableクラスには、DataRowCollectionクラスのオブジェクトがセットされたRowsプロパティがあり、そこから各DataRowオブジェクトを参照できる。
例えばDataTable型の変数dtがあるとすると、各レコード(各DataRowオブジェクト)はdt.Rows[0]、dt.Rows[1]、……として参照できる。
■DataRowクラスのインデクサ
特定のDataRowオブジェクトが取得できれば、後はそのインデクサによりレコード内の各カラム(各列)にアクセスできる。つまり、DataRow型の変数drがあるとすると、各カラムの値は、dr[0]、dr[1]、……としてアクセスできる。
以上の3つをつなぎ合わせれば、例えばデータセット内の最初のテーブルの、最初のレコードの第1カラムのデータは、
ds.Tables[0].Rows[0][0]
と記述できることがお分かりいただけるだろうか。
■DataTableクラスのColumnsプロパティ
テーブルの列情報を表すDataColumnオブジェクトについても簡単に見ておこう。DataTableクラスには、Rowsプロパティと同様にColumnsプロパティ(DataColumnCollection型)があり、各DataColumnオブジェクトをdc.Columns[0]、dc.Columns[1]、……として参照できる。DataColumnクラスは、列名を示すColumnNameプロパティや、その列に格納されているデータ型を示すDataTypeプロパティなどを持っている。
データセットを構成するオブジェクト階層を踏まえた上で、今度はコマンド・プロンプトで動作するサンプル・プログラムを示す。データベースのアクセスを行うMakeDataSetメソッドは先の2つのサンプル・プログラムと同じものだ。
// dataset.cs
using System;
using System.Data;
using System.Data.SqlClient;
public class ShowDataSet {
static DataSet MakeDataSet() {
string sqlStr = "SELECT pub_id, pub_name FROM publishers";
string connStr = "Server=(local)\\NetSDK;"
+ "Trusted_Connection=yes;"
+ "database=pubs";
SqlDataAdapter da = new SqlDataAdapter(sqlStr, connStr);
DataSet ds = new DataSet();
da.Fill(ds);
return ds;
}
public static void Main() {
DataSet ds = MakeDataSet();
DataTable dt = ds.Tables[0];
DataColumnCollection columns = dt.Columns;
DataRowCollection rows = dt.Rows;
Console.WriteLine("{0}\t{1}",
columns[0].ColumnName, columns[1].ColumnName);
Console.WriteLine("-----------------------------");
for (int r = 0; r < rows.Count; r++) {
for (int c = 0; c < columns.Count; c++) {
Console.Write(rows[r][c] + "\t");
}
Console.WriteLine();
}
}
}
// コンパイル方法:csc dataset.cs
DataColumnCollectionクラス、DataRowCollectionクラスともに、保持しているオブジェクトの要素数を示すCountプロパティを持っている。このサンプル・プログラムではこの値をループの回数として、インデックス番号により各レコードの各カラムの値にアクセスしている。
プログラムの実行結果は次の画面のようになる。
ここまでの説明では、DataRowオブジェクトの各列の値をインデックス番号によりアクセスしてきたが、この指定方法ではselect文における列の記述順序や、実際のテーブルの列のならびに依存してしまってあまり好ましくない。このため、DataRowクラスのインデクサには、列の名前を指定して値を取り出す手段も用意されている。この方法を用いると、
ds.Tables[0].Rows[0][0]
ds.Tables[0].Rows[0][1]
などは、
ds.Tables[0].Rows[0]["pub_id"]
ds.Tables[0].Rows[0]["pub_name"]
というふうに記述できる。「pub_id」や「pub_name」は実際のテーブル(この場合にはpublishersテーブル)にある列の名前だ。
■DataColumnオブジェクトによるインデックス
DataRowクラスのインデクサは、数値と文字列以外にも、インデックスとしてDataColumnオブジェクトを指定することもできる。そのため例えば、
ds.Tables[0].Rows[0][0]
ds.Tables[0].Rows[0][1]
などは、
ds.Tables[0].Rows[0][ds.Tables[0].Columns[0]]
ds.Tables[0].Rows[0][ds.Tables[0].Columns[1]]
とも記述できる。上記のコンソール版のサンプル・プログラム(dataset.cs)では、for文とインデックス番号により各レコードの値を表示していた。
DataColumnCollection columns = dt.Columns;
DataRowCollection rows = dt.Rows;
for (int r = 0; r < rows.Count; r++) {
for (int c = 0; c < columns.Count; c++) {
Console.Write(rows[r][c] + "\t");
}
Console.WriteLine();
}
この処理は、任意のDataTableオブジェクト内にあるすべてのレコード値を表示するものだが、DataColumnオブジェクトによるインデックスとforeach文を使用すれば、次のようにすっきりと記述することができる。
foreach (DataRow dr in dt.Rows) {
foreach (DataColumn dc in dt.Columns) {
Console.Write(dr[dc] + "\t");
}
Console.WriteLine();
}
■DataTableオブジェクトの名前
DataTableCollectionクラスのTablesプロパティによるDataTableオブジェクトへの参照も、テーブルに付けた名前を利用することができる。このためには、あらかじめDataTalbleオブジェクトに名前を付けておく必要がある。通常は次のようにして、Fillメソッドの呼び出し時にその第2パラメータでデータセット内に自動作成されるDataTableオブジェクトに名前を付けておく。
da.Fill(ds, "publishers");
この呼び出しを実行すると、データセット内に作成されるテーブル(ds.Tables[0])の名前が「publishers」になる。これにより、
ds.Tables["publishers"].Rows[0]["pub_id"]
ds.Tables["publishers"].Rows[0]["pub_name"]
といった記述が可能になる。なお、テーブル名を指定せずにFillメソッドを実行した場合には、「Table」という名前が勝手に付く。
以上、今回はデータセットおよびデータアダプタの役割や構造を解説し、これらを利用したレコード取得のプログラミングについて見てきた。.NETでデータベースを扱う目的では、前回までで解説した.NETデータ・プロバイダによるプログラミングと、今回のデータセット&データアダプタによるプログラミングの2種類が用意されているということになる。どちらを選択するかについては実は難しい問題なのだが、リファレンス・マニュアルにも「Web データ アクセス ストラテジに関する推奨事項」などのヒントが掲載されているので参考にしていただきたい。
さて次回は、データセットを利用した場合のテーブルの更新について解説する予定だ。
Copyright© Digital Advantage Corp. All Rights Reserved.