今回から数回に分けて、C# からデータベースへアクセスする方法を扱います。
現代の .NET では、Entity Framework Core のような ORM を使う場面が多くあります。
それでも、ADO.NET の基本を理解しておくことはとても重要です。
なぜなら、ORM の裏側でも最終的には接続、コマンド、パラメーター、読み取り、トランザクションといった低レベルなデータアクセスの考え方が動いているからです。
ADO.NET とは
ADO.NET は、.NET からリレーショナルデータベースへアクセスするための基本 API 群です。
主に次のような処理を行います。
- データベースへ接続する
- SQL を実行する
- 結果を 1 行ずつ読み取る
- INSERT / UPDATE / DELETE を実行する
- パラメーターを使って安全に値を渡す
- トランザクションで処理をまとめる
- 大量データを効率よく投入する
ADO.NET は ORM ではありません。
SQL と接続操作を比較的直接扱う、低レベルで明示的なデータアクセス API です。
古いデータアクセス技術との違い
過去の Windows 開発では、COM ベースのデータアクセス技術が使われていました。
ADO.NET は .NET 向けに設計された仕組みで、型安全性、マネージドコード、例外処理、データプロバイダーごとの実装分離などを持っています。
ADO.NET の特徴は、接続先ごとに専用のプロバイダーを使う点です。
たとえば SQL Server なら SQL Server 用の型、SQLite なら SQLite 用の型を使います。
ただし、それらは共通インターフェイスを実装しているため、ある程度抽象化できます。
データプロバイダーとは
データプロバイダーは、特定のデータベースへ接続するための実装セットです。
SQL Server、SQLite、PostgreSQL、MySQL など、データベースごとに接続方法や SQL 方言、パラメーター表記、機能が少しずつ異なります。
ADO.NET では、それぞれの違いをプロバイダーが吸収します。
SQL Server では、一般に Microsoft.Data.SqlClient を使います。
dotnet add package Microsoft.Data.SqlClient
コードでは次のように使います。
using Microsoft.Data.SqlClient;
string connectionString =
"Server=localhost;Database=AutoLot;Trusted_Connection=True;TrustServerCertificate=True;";
using SqlConnection connection = new SqlConnection(connectionString);
await connection.OpenAsync();
Console.WriteLine("接続しました。");
SqlConnection は SQL Server 専用の接続クラスです。
System.Data の主要なインターフェイス
ADO.NET には、プロバイダーごとの具象型とは別に、共通化されたインターフェイスがあります。
IDbConnection
データベース接続を表します。
接続文字列、接続開始、接続終了、トランザクション開始などを扱います。
IDbTransaction
トランザクションを表します。
複数の SQL 操作を 1 つの成功/失敗単位としてまとめます。
IDbCommand
実行する SQL やストアドプロシージャを表します。
接続、SQL 文字列、パラメーター、トランザクションなどを持ちます。
IDbDataParameter / IDataParameter
SQL に渡すパラメーターを表します。
SQL インジェクション対策や型安全な値渡しに重要です。
IDbDataAdapter / IDataAdapter
データベースとメモリ上のデータ表現を橋渡しするための型です。
現在の新規開発では、DataSet とセットで使う古典的な使い方が中心です。
IDataReader / IDataRecord
SQL の結果を前方へ 1 行ずつ読み取るための型です。
大量データを効率よく読むときによく使います。
接続、コマンド、読み取りの基本形
ADO.NET の最小構成は、接続、コマンド、読み取りです。
using Microsoft.Data.SqlClient;
string connectionString =
"Server=localhost;Database=AutoLot;Trusted_Connection=True;TrustServerCertificate=True;";
await using SqlConnection connection = new SqlConnection(connectionString);
await connection.OpenAsync();
await using SqlCommand command = connection.CreateCommand();
command.CommandText = """
SELECT Id, Make, Color, PetName
FROM Inventory
ORDER BY Id
""";
await using SqlDataReader reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
int id = reader.GetInt32(0);
string make = reader.GetString(1);
string color = reader.GetString(2);
string petName = reader.GetString(3);
Console.WriteLine($"{id}: {color} {make} ({petName})");
}
流れは次の通りです。
- 接続文字列を用意する
- 接続オブジェクトを作る
- 接続を開く
- コマンドを作る
- SQL を設定する
- 読み取りオブジェクトを取得する
- 1 行ずつ読む
この形は、この先のすべてのデータアクセス処理の基本になります。
ExecuteReader、ExecuteScalar、ExecuteNonQuery
コマンドには、用途ごとに代表的な実行メソッドがあります。
ExecuteReader
SELECT のように複数行の結果を読み取る場合に使います。
await using SqlDataReader reader = await command.ExecuteReaderAsync();
ExecuteScalar
1 つの値だけ取得したい場合に使います。
command.CommandText = "SELECT COUNT(*) FROM Inventory";
int count = (int)await command.ExecuteScalarAsync();
ExecuteNonQuery
INSERT / UPDATE / DELETE のように、結果セットを返さない SQL に使います。
command.CommandText = "DELETE FROM Inventory WHERE Id = 10";
int affectedRows = await command.ExecuteNonQueryAsync();
戻り値は、影響を受けた行数です。
なぜ低レベル API を学ぶのか
ORM を使うと、SQL を直接書かずにデータを扱える場面があります。
それでも ADO.NET を知る価値はあります。
- SQL の実行コストを理解できる
- 接続やトランザクションの境界を意識できる
- パラメーター化の重要性が分かる
- ORM で問題が起きたときに原因を追いやすい
- 大量投入や特殊な SQL を直接扱える
ADO.NET は、アプリケーションとデータベースの間にある「薄いが重要な層」です。
まとめ
ADO.NET は、.NET からデータベースへアクセスするための基本 API です。
接続、コマンド、パラメーター、読み取り、トランザクションという考え方を直接扱います。
まずは、接続を開き、SQL を実行し、結果を読む流れを押さえましょう。
次回は、SQL Server を使う前提で、サンプル用のデータベースとテーブルを用意する流れを整理します。