bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] データプロバイダーを抽象化して扱う

[C#] データプロバイダーを抽象化して扱う

Jul 21, 2026 C# , .NET , Data Access bucket-sort

前回は、SQL Server にサンプルデータベースを用意しました。

今回は、ADO.NET のデータプロバイダーを抽象化して扱う方法を見ていきます。

SQL Server だけを使うアプリケーションなら、SqlConnection、SqlCommand、SqlDataReader を直接使えば十分です。
しかし、複数のデータベースに対応したいツールやライブラリでは、具象型に依存しすぎると差し替えが難しくなります。

具象型に直接依存する書き方

SQL Server 専用に書くと、次のようになります。

using Microsoft.Data.SqlClient;

await using SqlConnection connection = new SqlConnection(connectionString);
await connection.OpenAsync();

await using SqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT COUNT(*) FROM Inventory";

int count = (int)await command.ExecuteScalarAsync();

このコードは分かりやすく、SQL Server 固有の機能も使いやすいです。

一方で、SQLite や PostgreSQL に切り替えたい場合、型名や接続文字列、パラメーター表記などを変更する必要があります。

共通インターフェイスで見る

ADO.NET の接続やコマンドは、共通インターフェイスを実装しています。

using System.Data;
using Microsoft.Data.SqlClient;

IDbConnection connection = new SqlConnection(connectionString);

connection.Open();

IDbCommand command = connection.CreateCommand();
command.CommandText = "SELECT COUNT(*) FROM Inventory";

object? result = command.ExecuteScalar();

Console.WriteLine(result);

この形にすると、呼び出し側は IDbConnection や IDbCommand として扱えます。

ただし、インターフェイスだけでは具体的な接続オブジェクトの生成までは抽象化できません。
生成部分では、結局 new SqlConnection(...) が出てきます。

DbProviderFactory の考え方

DbProviderFactory は、接続、コマンド、パラメーターなどを作成するファクトリです。

SQL Server 用のファクトリを使うと、次のように書けます。

using System.Data.Common;
using Microsoft.Data.SqlClient;

DbProviderFactory factory = SqlClientFactory.Instance;

await using DbConnection connection = factory.CreateConnection()
    ?? throw new InvalidOperationException("接続を作成できません。");

connection.ConnectionString = connectionString;
await connection.OpenAsync();

await using DbCommand command = connection.CreateCommand();
command.CommandText = "SELECT COUNT(*) FROM Inventory";

object? result = await command.ExecuteScalarAsync();

Console.WriteLine(result);

このコードでは、DbConnection、DbCommand という共通の抽象クラスで扱っています。

設定からプロバイダーを切り替える

プロバイダー名と接続文字列を設定へ持たせると、切り替えやすくなります。

{
  "Database": {
    "Provider": "Microsoft.Data.SqlClient",
    "ConnectionString": "Server=localhost,1433;Database=AutoLot;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;"
  }
}

ファクトリを選ぶ処理です。

using System.Data.Common;
using Microsoft.Data.SqlClient;

static DbProviderFactory GetFactory(string providerName)
{
    return providerName switch
    {
        "Microsoft.Data.SqlClient" => SqlClientFactory.Instance,
        _ => throw new NotSupportedException(
            $"未対応のプロバイダーです: {providerName}")
    };
}

利用側です。

string providerName = configuration["Database:Provider"]
    ?? throw new InvalidOperationException("プロバイダー名がありません。");

string connectionString = configuration["Database:ConnectionString"]
    ?? throw new InvalidOperationException("接続文字列がありません。");

DbProviderFactory factory = GetFactory(providerName);

await using DbConnection connection = factory.CreateConnection()
    ?? throw new InvalidOperationException("接続を作成できません。");

connection.ConnectionString = connectionString;
await connection.OpenAsync();

この形にすると、アプリケーション本体の多くのコードを共通化できます。

パラメーターもファクトリで作る

コマンドのパラメーターもファクトリから作れます。

await using DbCommand command = connection.CreateCommand();
command.CommandText = """
    SELECT Id, Make, Color, PetName
    FROM Inventory
    WHERE Make = @make
    """;

DbParameter parameter = command.CreateParameter();
parameter.ParameterName = "@make";
parameter.Value = "Honda";

command.Parameters.Add(parameter);

await using DbDataReader reader = await command.ExecuteReaderAsync();

while (await reader.ReadAsync())
{
    Console.WriteLine(reader["PetName"]);
}

パラメーター名の表記は、プロバイダーによって違いが出ることがあります。
SQL Server では @name 形式が一般的ですが、すべてのデータベースで完全に同じとは限りません。

抽象化の利点

データプロバイダーを抽象化すると、次の利点があります。

  • 複数データベース対応の入口を作れる
  • テスト用に別プロバイダーへ差し替えやすい
  • ライブラリ側で特定データベースへの依存を減らせる
  • 接続・コマンド生成の責務を集約できる

特に、ツールやフレームワーク的なコードでは有効です。

抽象化の欠点

一方で、抽象化には欠点もあります。

  • 各データベース固有の機能を使いにくい
  • SQL 方言の違いは消えない
  • パラメーター表記やデータ型の違いは残る
  • コードが少し複雑になる
  • 結局、完全な移植性は簡単ではない

「どのデータベースでも同じ SQL が動く」と期待しすぎると危険です。
抽象化できるのは、接続やコマンド生成の形であって、SQL の意味まですべて共通化できるわけではありません。

実務での判断

SQL Server だけを使う業務アプリなら、SqlConnection などの具象型を直接使う方が読みやすいことが多いです。

await using SqlConnection connection = new SqlConnection(connectionString);

一方、複数プロバイダー対応の共通ライブラリや、接続先を切り替える診断ツールでは、DbProviderFactory が役立ちます。

抽象化は目的があって初めて価値を持ちます。
将来使うかもしれない、という理由だけで全コードを抽象化すると、かえって保守しづらくなります。

まとめ

ADO.NET には、プロバイダー固有の具象型と、共通化されたインターフェイス・抽象クラスがあります。

DbProviderFactory を使うと、接続、コマンド、パラメーターの生成をプロバイダーごとに切り替えられます。
ただし、SQL 方言やデータ型の違いまでは完全に隠せません。

次回は、SQL Server を対象に、接続、コマンド、読み取り処理をもう少し実践的に掘り下げます。

C# .NET ADO.NET DbProviderFactory System.Data データプロバイダー
← [C#] SQL Server に接続するためのサンプルデータベースを用意する [C#] 接続・コマンド・データ読み取りを深掘りする →

Related Posts

  • [C#] ADO.NET によるデータアクセスの全体像を理解する Jul 19, 2026
  • [C#] 大量データを効率よく投入する Jul 24, 2026
  • [C#] 追加・更新・削除とトランザクションを理解する Jul 23, 2026
  • [C#] 接続・コマンド・データ読み取りを深掘りする Jul 22, 2026

Table of Contents

  • 具象型に直接依存する書き方
  • 共通インターフェイスで見る
  • DbProviderFactory の考え方
  • 設定からプロバイダーを切り替える
  • パラメーターもファクトリで作る
  • 抽象化の利点
  • 抽象化の欠点
  • 実務での判断
  • まとめ

Recent Posts

  • [C#] EF Core でデータベースの準備と初期データを扱う Aug 8, 2026
  • [C#] EF Core とリポジトリでデータ操作を整理する Aug 7, 2026
  • [C#] DbContext の設定と保存処理を拡張する Aug 6, 2026
  • [C#] EF Core のエンティティと表示用モデルを設計する Aug 5, 2026
  • [C#] EF Core を中心にデータアクセス層を分ける Aug 4, 2026

Categories

  • C#150
  • .NET149
  • AWS27
  • Laravel16
  • Entity Framework Core15
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • Data Access6
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Diagnostics1
  • Filament1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • コレクション
  • PHP
  • Entity Framework Core
  • セキュリティ
  • MySQL
  • Linux
  • パフォーマンス
  • Apache
  • LINQ
  • System.Collections.Generic
  • デリゲート
  • リフレクション
  • ADO.NET
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • System.Collections
  • Windows
  • メタデータ
  • メモリ管理
  • CIL
  • DoS
  • Nginx
  • SQL Server
  • WordPress
  • ラムダ式
  • 監視
  • 設計
  • Amazon Linux 2023
  • Delegate
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • LINQ to Objects
  • OPCache
  • Pointer
  • Reflection
  • System.Collections.Specialized
  • Unsafe
  • Webサーバー
  • アセンブリ
  • インターフェース
  • オブジェクト指向
Powered by Hugo & Explore Theme.