C#のDataReaderとDataAdapterの違いを徹底解説!ADO.NETのデータベース操作を完全マスター
生徒
「C#でデータベースからデータを取得する方法って、いくつかあるんですか?」
先生
「はい、ADO.NETというデータベース操作の仕組みでは、主にDataReaderとDataAdapterという2つの方法があります。」
生徒
「2つもあるんですね。どう違うんですか?使い分けとかありますか?」
先生
「それぞれ特徴があって、状況によって使い分けるんです。それでは、詳しく見ていきましょう!」
1. DataReaderとDataAdapterとは?基本を理解しよう
C#でデータベースを操作する際に使うADO.NET(エーディーオー・ドット・ネット)には、データを取得する方法がいくつかあります。その中でも代表的なのがDataReader(データリーダー)とDataAdapter(データアダプター)です。
これらは両方ともデータベースからデータを取得するために使いますが、動作の仕組みや使い方が大きく異なります。ADO.NETとは、マイクロソフトが提供するデータベースにアクセスするための技術で、SQL ServerやMySQL、Oracleなど様々なデータベースに接続できます。
まず、それぞれの特徴を簡単に説明すると、DataReaderは「高速で軽量」、DataAdapterは「柔軟で多機能」という違いがあります。この違いを理解することで、適切な方法を選択できるようになります。
2. DataReaderの特徴と使い方
DataReaderは、データベースから取得したデータを順番に一方向で読み取るための仕組みです。例えるなら、カセットテープのように「早送りはできるけど、巻き戻しはできない」というイメージです。
DataReaderの主な特徴は以下の通りです:
- 高速:データを読み取るスピードが速い
- 軽量:メモリの使用量が少ない
- 接続型:データベースとの接続を保ったまま操作する
- 読み取り専用:データの参照だけができる(更新はできない)
- 前方向のみ:データを順番に読み進むことしかできない
DataReaderを使う場合は、データベースに接続したまま処理を行います。これを「接続型アクセス」と呼びます。接続したままなので、処理が終わったらすぐに接続を閉じる必要があります。
それでは、実際のコードで見てみましょう。以下は、SQL Serverのデータベースから顧客情報を取得する例です:
using System;
using System.Data.SqlClient;
class Program
{
static void Main()
{
// データベースへの接続文字列
string connectionString = "Server=localhost;Database=SampleDB;Trusted_Connection=True;";
// SqlConnectionオブジェクトを作成
using (SqlConnection connection = new SqlConnection(connectionString))
{
// データベースに接続
connection.Open();
// SQL文を作成
string sql = "SELECT CustomerID, CustomerName FROM Customers";
// SqlCommandオブジェクトを作成
SqlCommand command = new SqlCommand(sql, connection);
// DataReaderを使ってデータを取得
using (SqlDataReader reader = command.ExecuteReader())
{
// データが存在する限り繰り返す
while (reader.Read())
{
// データを取得して表示
int id = reader.GetInt32(0); // 1列目を取得
string name = reader.GetString(1); // 2列目を取得
Console.WriteLine($"ID: {id}, 名前: {name}");
}
}
// usingブロックを抜けると自動的に接続が閉じられる
}
}
}
このコードでは、ExecuteReader()メソッドを呼び出してDataReaderを取得し、Read()メソッドで1行ずつデータを読み取っています。Read()メソッドは、次のデータがある場合はtrueを返し、データがなくなるとfalseを返します。
DataReaderは、大量のデータを高速に読み取りたい場合や、単純にデータを表示するだけの場合に最適です。
3. DataAdapterの特徴と使い方
DataAdapterは、データベースとDataSetやDataTableというデータの入れ物の間を橋渡しする役割を持っています。例えるなら、「トラックでデータを運んで、倉庫に保管する」というイメージです。
DataAdapterの主な特徴は以下の通りです:
- 非接続型:データを取得したらデータベースとの接続を切断できる
- DataSetと連携:メモリ上にデータを保持できる
- 更新が可能:データの追加、更新、削除ができる
- 自由なアクセス:データを何度でも読み返せる
- バッチ更新:複数の変更をまとめてデータベースに反映できる
DataAdapterを使う場合、データは一度メモリ上のDataSetやDataTableに読み込まれます。これにより、データベース接続を切断した後も自由にデータを操作できます。これを「非接続型アクセス」と呼びます。
それでは、DataAdapterを使った実際のコードを見てみましょう:
using System;
using System.Data;
using System.Data.SqlClient;
class Program
{
static void Main()
{
// データベースへの接続文字列
string connectionString = "Server=localhost;Database=SampleDB;Trusted_Connection=True;";
// SqlConnectionオブジェクトを作成
using (SqlConnection connection = new SqlConnection(connectionString))
{
// SQL文を作成
string sql = "SELECT ProductID, ProductName, Price FROM Products";
// SqlDataAdapterオブジェクトを作成
SqlDataAdapter adapter = new SqlDataAdapter(sql, connection);
// DataTableオブジェクトを作成(データの入れ物)
DataTable dataTable = new DataTable();
// DataAdapterを使ってデータをDataTableに格納
adapter.Fill(dataTable);
// この時点でデータベース接続は自動的に閉じられている
// DataTableからデータを取り出して表示
foreach (DataRow row in dataTable.Rows)
{
int id = Convert.ToInt32(row["ProductID"]);
string name = row["ProductName"].ToString();
decimal price = Convert.ToDecimal(row["Price"]);
Console.WriteLine($"商品ID: {id}, 商品名: {name}, 価格: {price}円");
}
// データを自由に何度でも読み返せる
Console.WriteLine($"\n取得した商品数: {dataTable.Rows.Count}件");
}
}
}
このコードでは、Fill()メソッドを呼び出すだけで、データベースへの接続、データの取得、接続の切断が自動的に行われます。取得したデータはDataTableに保存されるので、何度でも自由にアクセスできます。
DataAdapterは、データを編集したり、複数のテーブルを扱ったり、オフラインでデータを操作したい場合に便利です。
4. DataReaderとDataAdapterの違いを比較表で理解する
DataReaderとDataAdapterの違いを、わかりやすく表にまとめました:
| 比較項目 | DataReader | DataAdapter |
|---|---|---|
| 接続方式 | 接続型(Connected) | 非接続型(Disconnected) |
| データの読み取り | 前方向のみ(一方通行) | 自由にアクセス可能 |
| 速度 | 高速 | やや遅い |
| メモリ使用量 | 少ない | 多い |
| データの更新 | 不可(読み取り専用) | 可能 |
| データの保持 | 保持しない | DataSet/DataTableに保持 |
| 使用するクラス | SqlDataReaderなど | SqlDataAdapterなど |
| 適した用途 | 大量データの読み取り、表示のみ | データ編集、複雑な操作 |
この表を見ると、DataReaderは「スピード重視でシンプル」、DataAdapterは「柔軟性重視で多機能」という特徴があることがわかります。
5. 接続型と非接続型の違いを詳しく知ろう
DataReaderとDataAdapterの最も大きな違いは、接続型か非接続型かという点です。これを理解することで、どちらを使うべきかが判断しやすくなります。
接続型(Connected)とは
接続型は、データベースに接続したままデータを操作する方式です。DataReaderがこの方式を採用しています。蛇口をひねって水を出し続けているような状態で、使い終わったらすぐに蛇口を閉める必要があります。
接続型のメリットは、データベースから直接データを読み取るため高速で、メモリの使用量も少ないことです。デメリットは、データベースとの接続を占有してしまうため、長時間の処理には向きません。
非接続型(Disconnected)とは
非接続型は、必要なデータを一度取得したら、データベースとの接続を切断して、メモリ上でデータを操作する方式です。DataAdapterがこの方式を採用しています。お店で商品を買って家に持ち帰り、家でゆっくり使うようなイメージです。
非接続型のメリットは、データベースの接続を長時間占有しないため、複数のユーザーが同時にアクセスするアプリケーションに適していることです。また、オフライン(ネットワークに繋がっていない状態)でもデータを操作できます。デメリットは、すべてのデータをメモリに読み込むため、大量のデータを扱う場合はメモリを多く消費することです。
6. DataReaderを使うべき場面
DataReaderは、以下のような場面で使うと効果的です:
- 大量のデータを読み取る場合:数万件、数十万件のレコードを処理する際、メモリ使用量を抑えられます
- データを一度だけ読めばよい場合:レポート出力やログ表示など、読み取るだけの処理
- パフォーマンスが重要な場合:高速な処理が求められるシステム
- リアルタイムでデータを表示する場合:検索結果をすぐに画面に表示したい時
例えば、ECサイトで商品の一覧を表示する場合、ユーザーはただ商品を見るだけなので、DataReaderを使うのが適しています。
using System;
using System.Data.SqlClient;
class Program
{
static void Main()
{
string connectionString = "Server=localhost;Database=ShopDB;Trusted_Connection=True;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// 在庫がある商品のみを取得
string sql = "SELECT ProductName, Price, Stock FROM Products WHERE Stock > 0";
SqlCommand command = new SqlCommand(sql, connection);
using (SqlDataReader reader = command.ExecuteReader())
{
Console.WriteLine("【在庫あり商品一覧】");
Console.WriteLine("------------------------");
while (reader.Read())
{
string productName = reader.GetString(0);
decimal price = reader.GetDecimal(1);
int stock = reader.GetInt32(2);
Console.WriteLine($"商品名: {productName}");
Console.WriteLine($"価格: {price:C}");
Console.WriteLine($"在庫数: {stock}個");
Console.WriteLine("------------------------");
}
}
}
}
}
このコードは在庫のある商品を表示するだけなので、DataReaderが最適です。データを読み取ったらすぐに表示し、処理が終わったら接続を閉じます。
7. DataAdapterを使うべき場面
DataAdapterは、以下のような場面で使うと効果的です:
- データを更新する必要がある場合:ユーザーがデータを編集して保存する機能
- 複数のテーブルを扱う場合:複雑なデータ構造を操作する時
- オフラインでデータを操作したい場合:ネットワーク接続がない環境でも作業できる
- データを何度も参照する場合:同じデータに繰り返しアクセスする処理
- DataGridViewなどのコントロールにバインドする場合:Windowsフォームアプリケーションで表を表示する時
例えば、従業員管理システムで従業員情報を編集して保存する場合、DataAdapterが適しています:
using System;
using System.Data;
using System.Data.SqlClient;
class Program
{
static void Main()
{
string connectionString = "Server=localhost;Database=CompanyDB;Trusted_Connection=True;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
// 従業員データを取得
string selectSql = "SELECT EmployeeID, EmployeeName, Salary FROM Employees";
SqlDataAdapter adapter = new SqlDataAdapter(selectSql, connection);
// 更新用のコマンドを自動生成
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
DataTable employeeTable = new DataTable();
adapter.Fill(employeeTable);
// データを表示
Console.WriteLine("【従業員一覧】");
foreach (DataRow row in employeeTable.Rows)
{
Console.WriteLine($"ID: {row["EmployeeID"]}, 名前: {row["EmployeeName"]}, 給与: {row["Salary"]}円");
}
// データを更新(例:ID=1の従業員の給与を変更)
DataRow[] targetRows = employeeTable.Select("EmployeeID = 1");
if (targetRows.Length > 0)
{
targetRows[0]["Salary"] = 350000;
Console.WriteLine("\n給与を更新しました。");
}
// 変更をデータベースに反映
adapter.Update(employeeTable);
Console.WriteLine("データベースに保存しました。");
}
}
}
このコードでは、DataTableにデータを読み込んでから編集し、最後にUpdate()メソッドでデータベースに変更を保存しています。このような更新処理にはDataAdapterが必須です。
8. パフォーマンスとメモリ使用量の違い
DataReaderとDataAdapterでは、パフォーマンス(処理速度)とメモリ使用量に大きな違いがあります。これを理解すると、システム設計の際に適切な選択ができます。
DataReaderのパフォーマンス
DataReaderは、データベースから直接データを読み取るため、非常に高速です。1行ずつ順番に処理するため、メモリには現在読み取っている1行分のデータしか保持しません。100万件のデータを処理する場合でも、メモリ使用量はほぼ一定です。
ただし、データベースとの接続を保ったまま処理するため、処理に時間がかかると他のユーザーに影響を与える可能性があります。
DataAdapterのパフォーマンス
DataAdapterは、すべてのデータをメモリ上のDataTableに読み込むため、データ量が多いとメモリを大量に消費します。100万件のデータを読み込むと、メモリが不足してプログラムが停止する可能性もあります。
しかし、一度データを読み込んでしまえば、データベース接続を切断できるため、複数のユーザーが同時にアクセスするシステムでは有利です。また、メモリ上でデータを操作できるため、複雑な処理や繰り返し参照する処理では高速です。
メモリとは、パソコンがプログラムを実行する際に一時的にデータを保存する場所です。メモリの容量が大きいほど、多くのデータを同時に扱えます。メモリが不足すると、パソコンの動作が遅くなったり、プログラムがエラーになったりします。
9. 実践的な使い分けの判断基準
実際のプロジェクトで、DataReaderとDataAdapterのどちらを使うべきか迷った時の判断基準を紹介します:
DataReaderを選ぶ基準
- 取得するデータ量が非常に多い(数万件以上)
- データを表示するだけで、編集や更新は不要
- 処理速度を最優先したい
- メモリの使用量を抑えたい
- レポート作成やログ出力など、一方向の処理
DataAdapterを選ぶ基準
- データの追加、更新、削除が必要
- 同じデータを何度も参照する
- 複数のテーブルのデータを組み合わせて処理する
- DataGridViewなどのUIコントロールと連携する
- オフラインでデータを操作したい
- バッチ処理でまとめて更新したい
どちらの方法を選んでも、必ずusingステートメントやClose()メソッドを使って、リソースを適切に解放することが重要です。接続やリーダーを開きっぱなしにすると、データベースに負荷がかかり、他のユーザーがアクセスできなくなる可能性があります。
10. Entity Frameworkとの関係
最後に、Entity Framework(エンティティ フレームワーク)との関係について触れておきます。Entity Frameworkは、ADO.NETをさらに使いやすくした、より高度なデータベース操作の仕組みです。
Entity Frameworkを使うと、DataReaderやDataAdapterを直接使わなくても、簡単にデータベース操作ができます。しかし、内部的にはADO.NETの仕組みを使っているため、DataReaderとDataAdapterの違いを理解しておくことは重要です。
特に、Entity Frameworkでも「即座にデータを取得する」か「遅延読み込みをする」かという選択肢があり、これはDataReaderとDataAdapterの違いに通じるものがあります。基礎をしっかり理解することで、より高度な技術も使いこなせるようになります。
ポイント
ADO.NETのDataReaderとDataAdapterは、それぞれ異なる目的と特徴を持っています。DataReaderは高速で軽量な読み取り専用の接続型アクセス、DataAdapterは柔軟で多機能な非接続型アクセスです。用途に応じて適切に使い分けることで、効率的なデータベースアプリケーションを開発できます。