最近、少し前に開発されたASP.NET Web Formsのアプリケーションを仕事でメンテナンスしてます。
そこでDataSet(System.Data.DataSet)を初めて扱ったのですが、わからないことが多くて時間を費やしてしまったので、少し勉強することにしました。
DataSet=メモリ上にデータベースを構成するクラス
MSDN.aspx)ではデータのメモリ内のキャッシュと説明されていますが、これではちょっとイメージしづらいです。
元々はデータベースから取得したデータ(テーブルやレコードなど)をメモリ上でも扱えるようにするデータ構造なので、「メモリにデータベースを構成するクラス(群)」と言ったほうが捉えやすくなるかと思います。
DBなので複数のテーブルを持ち、1つのテーブルは複数のカラムと行からなる包含関係を持ちます。
- System.Data.DataSetクラス(=DB)
- System.Data.DataTableクラス(=テーブル)
- System.DataColumnクラス(=列・カラム)
- System.DataRowクラス(=行・レコード)
基本的なデータの作成と参照
上の通り包含関係となっているので、まずはDataSetを作成する必要があります。
var company = new DataSet();
次にDataTableとDataColumn定義してDataSetへ追加します。
ここまでがDDLに相当します。
var employees = new DataTable("Employees");
var id = new DataColumn("Id", typeof(string));
employees.Columns.Add(id);
var name = new DataColumn("Name", typeof(string));
employees.Columns.Add(name);
var age = new DataColumn("Age", typeof(uint));
employees.Columns.Add(age);
company.Tables.Add(employees);
最後にレコードをDataTableのRowsプロパティへ挿入します。
- DataRowのインスタンスはDataTableのNewRowメソッドで取得
- 取得したDataRowインスタンスのインデクサ([])でカラムを指定して値を設定
var employee1 = employees.NewRow();
employee1["Id"] = "000001";
employee1["Name"] = "荒岩 一味";
employee1["Age"] = 40;
var employee2 = employees.NewRow();
employee2[0] = "000002";
employee2[1] = "田中 一郎";
employee2[2] = 29;
employees.Rows.Add(employee1);
employees.Rows.Add(employee2);
挿入したレコードの値を参照するためには、3段階でアクセスします。
- DataSetのTablesプロパティからDataTableのインスタンスを取得
- DataTableのRowsプロパティからDataRowのインスタンスを取得
- DataRowのインデクサでカラムを指定して値を取得
var idColumnName = company.Tables[0].Columns[0].ColumnName;
var nameColumnName = company.Tables[0].Columns[1].ColumnName;
var ageColumnName = company.Tables[0].Columns[2].ColumnName;
var r = company.Tables[0].Rows[0];
Console.WriteLine($"{idColumnName}:{r[0]} " +
$"{nameColumnName}:{r[1]} " +
$"{ageColumnName}:{r[2]}");
r = company.Tables["Employees"].Rows[1];
Console.WriteLine($"{idColumnName}:{r[idColumnName]} " +
$"{nameColumnName}:{r[nameColumnName]} " +
$"{ageColumnName}:{r[ageColumnName]}");
DataSetですが継承クラスとして定義する方法がVisual Studioで提供されています。
まずはソースコードファイルを追加するようにソリューションエクスプローラーを右クリックして追加を選び、「Visual C# アイテム」を選択し、「DataSet」アイテムを追加します。
追加すると、4つのファイルが生成されます。
重要な点として、.Designer.csファイルにはSystem.Data.DataSetを継承したCompanyDataSetクラスが新たに定義されています。
この中で、xsdファイルを選択するとデザイナが開きますので、DataTableを追加します。
DataTableにカラムを追加したり、プロパティで型やその他の制約を設定したりできます。
CompanyDataSetのメンバーを確認すると、DataSetのTablesプロパティに加えてEmployeesプロパティが追加されています。
さらにEmployeesDataTableクラスとEmployeeRowクラスがインナークラスとして定義されてます。
EmployeesDataTable(System.Data.TypedTableBaseを継承)ではDataColumnが、EmployeeRow(System.Data.DataRowを継承)ではカラムの値がそれぞれプロパティとなっており、プロパティを介してアクセスできるようになっています。
CompanyDataSetでデータを作成・参照するコードを示します。
DataSetデザイナを使わない先程の方法と比較して、異なる点が2点あります。
- DataSet、DataTable、DataColumn、DataRowを継承したクラスを使う
- プロパティを介してDataTable、DataColumn、DataRowの値へアクセスできる
- インデクサを使ったアクセスでは実行時にしかエラーとならないので、プロパティが使えることは利点
var companyDataSet = new CompanyDataSet();
var employee3 = companyDataSet.Employees.NewEmployeesRow();
employee3.Id = "000003";
employee3.Name = "広田 けいこ";
employee3.Age = 32;
companyDataSet.Employees.AddEmployeesRow(employee3);
var idColumnName = companyDataSet.Employees.IdColumn.ColumnName;
var nameColumnName = companyDataSet.Employees.NameColumn.ColumnName;
var ageColumnName = companyDataSet.Employees.AgeColumn.ColumnName;
var r = companyDataSet.Employees.Rows[0];
Console.WriteLine($"{idColumnName}:{r[idColumnName]} " +
$"{nameColumnName}:{r[nameColumnName]} " +
$"{ageColumnName}:{r[ageColumnName]}");
DataSetでLINQを使う
DataTableはコレクション構造の一種なので、LINQが使えそうな気がしてきます。
ですが、Rowsプロパティ(System.Data.DataRowCollectionクラス)はIEnumerableを実装していないので、そのままではLINQは使えません。
companyDataSet.Employees.Rows.Where(e => e.Age >= 30).Select(e => e.Name);
その代わり、DataTableクラスのAsEnumerableメソッドを使えば、IEnumerableを実装するEnumerableRowCollectionクラスが返されるので、LINQを使えるようになります。
var names =
companyDataSet.Employees.AsEnumerable()
.Where(e => e.Age >= 30).Select(e => e.Name);
foreach (var name in names)
{
Console.WriteLine(name);
}
まとめ
DataSetを使ったデータの定義や参照・更新について触れました。
データベースやASP.NET Web Formsとの連携などは、また別の機会に取り組みたいと思います。