ADO.NET 2.0 基本クラスおよびファクトリによる汎用的なコーディング

Bob Beauchemin
DevelopMentor

July 2004

適用対象 :
   Microsoft ADO.NET 2.0
   Microsoft Visual Studio 2005 および以前のバージョン
   C# プログラミング言語
   Visual Basic プログラミング言語

概要 : 汎用的なデータ アクセス コードを作成できる ADO.NET 2.0 の新機能について、さらに説明します。

関連する VSGeneric.exe サンプル コードのダウンロード

目次

はじめに
基本クラス
プロバイダ ファクトリ
構成情報の指定
データ ソースの列挙
接続文字列のビルド
その他の汎用的なコーディングについての考慮事項
総括

はじめに

Microsoft ADO.NET は、データベース汎用の API です。この API には、データベース ベンダが、OLE DB/ADO (プロバイダ)、ODBC (ドライバ)、JDBC (ドライバ) など、独自のデータベースのプラグイン データ プロバイダを記述します。この API は、dbLib などのようなデータベース固有の機能セット、あるいは、OO4O (Oracle Objects for OLE) などのようなデータベース固有のオブジェクト モデルを備えているものではありません。

従来の ADO.NET では、厳密な仕様により定義されたインターフェイス セットにプロバイダを分割していました。たとえば、各 OLE DB プロバイダの作成者は、インターフェイスの標準セットを実装し、必要なインターフェイスおよびオプションのインターフェイスを組み込んでいました。必要なインターフェイスは最も低レベルな共通のデノミネータを指定し、オプションのインターフェイスは高度なデータベースまたはプロバイダ機能用に定義されました。OLE DB 仕様では、プロバイダが新しいインターフェイスを追加してデータベース固有の機能をカプセル化することを許可していましたが、プログラマが OLE DB プロバイダと共に使用していた ADO ライブラリでは、一般にこれらのインターフェイスを使用していませんでした。

ADO.NET は最初から、プロバイダ作成者がデータベース固有の機能をサポートする空間を持てるように設計されました。プロバイダ作成者は、Connection クラス、Command クラス、DataReader クラスなど、一連のクラスを実装しました。Microsoft .NET ではクラスおよびインターフェイスが表示される (ADO ではインターフェイスのみがプログラマに表示) ため、ADO.NET のプロバイダ作成者は、一連のプロバイダ固有のクラスを実装して、インターフェイス経由で汎用的な機能を公開し、また、データベース固有の機能をクラス プロパティおよびメソッドとして実装します。プロバイダ作成者は独自のパラレルなクラス階層を実装していました。表 1 に、SqlClient データ プロバイダおよび OracleClient データ プロバイダのパラレルなクラス階層と、双方のデータ プロバイダにより実装されるインターフェイスを示します。これらのインターフェイスは System.Data 名前空間に定義されています。

表 1. ADO.NET 1.0/1.1 におけるプロバイダ固有クラスおよび汎用インターフェイス

SqlClient クラス Oracle クラス 汎用インターフェイス
SqlConnection OracleConnection IDbConnection
SqlCommand OracleCommand IDbCommand
SqlDataReader OracleDataReader IDataReader/IDataRecord
SqlTransaction OracleTransaction IDbTransaction
SqlParameter OracleParameter IDbDataParameter
SqlParameterCollection OracleParameterCollection IDataParameterCollection
SqlDataAdapter OracleDataAdapter IDbDataAdapter

ADO.NET 1.0 および 1.1 において、プログラマには 2 つの選択肢がありました。プロバイダ固有クラスにコーディングするか、または汎用インターフェイスにコーディングするかのどちらかです。会社のデータベースが、計画されたソフトウェア存続期間中に変更される可能性がある場合、あるいは、その製品が異なるデータベースを持つ顧客をサポートする商用パッケージである場合には、汎用インターフェイスでプログラミングする必要がありました。インターフェイスでコンストラクタを呼び出すことはできないので、ほとんどの汎用プログラムには、次のように、適切なプロバイダ固有クラスで "new" を呼び出すことにより、元の IDbConnection を取得するタスクを実行するコードが含まれていました。

enum provider {sqlserver, oracle, oledb, odbc};
public IDbConnection GetConnectionInterface()
{
// 構成からプロバイダを決定します
provider prov = GetProviderFromConfigFile();
IDbConnection conn = null;
switch (prov) {
  case provider.sqlserver: 
    conn = new SqlConnection(); break;
  case provider.oracle:     
    conn = new OracleConnection(); break;
 // アプリケーションが新しいプロバイダをサポートするため、これらを追加します 
 }
return conn;
}

上記で GetProviderFromConfigFile と呼ばれるメソッドは、構成ファイルにプロバイダ情報を格納するメカニズムのように、各社で手書きでコーディングされていました。さらに、複数のデータベースをサポートするベンダは、ユーザまたはソフトウェア プログラマがインストール時に使用するデータベースを選択できるように、ベンダ固有のコードを記述する必要がありました。

ADO.NET 2.0 では、プロバイダが実装できるように規定された一連の基本クラス、接続文字列のセットアップおよび処理、プロバイダ固有の接続やコマンドなどの取得を標準化するファクトリ クラスにより、構成、セットアップ、および汎用コーディングを体系化します。ユーザーが選択したデータベース上での動作をサポートするソフトウェア会社や、プロジェクトの存続期間中にデータベースが変更になる (Microsoft Access から Microsoft SQL Server への変更など) 可能性があると考えているプログラマにとっては、これらの機能により手間を省くことができます。

基本クラス

ADO.NET の各リリースと共に、新しい機能が "基本プロバイダ プロファイル" に追加されてきました。バージョン 1.1 では、DataReader に 0 以外の行数が含まれているかどうかを、プログラマが IDataReader.Read() を呼び出すことなく判断できるように、DataReader にプロパティを追加しました (HasRows プロパティ)。インターフェイスは変更できないため、このプロパティを単に IDataReader インターフェイスに追加することはできず、各プロバイダ固有のクラス (たとえば、SqlDataReaderOleDbDataReader など) に追加する必要がありました。通常、インターフェイスによるバージョニングを使用するには、新しいインターフェイスを定義します。この新しいインターフェイスは、元のインターフェイスを拡張する (たとえば IDataReader2 は IDataReader から継承され、HasRows を追加) か、または、新しい機能だけを組み込む (たとえば IDataReaderNewStuff は HasRows のみを含む) ことができます。

機能をバージョニングする方法としては、他に基本クラスを使用する方法があります。基本クラスを使用すると、クラス継承モデルが制限 (各クラスは、.NET 内の 1 つの基本クラスからしか継承できない) されますが、将来、機能セットを変更することが予想される場合には、このメカニズムが適しています。ADO.NET 2.0 には多くの新機能が含まれているため、モデルではインタフェースではなく基本クラスが使用されます。ただし、下位互換性を保つために、ADO.NET 1.0/1.1インターフェイスは維持されます。元の例を使用するため、ADO.NET 2.0 には HasRows プロパティを含む DbDataReader クラスが含まれます。将来、プロパティ、メソッド、およびイベントを追加することができます。基本クラスの概念に基づく唯一の ADO.NET 1.0/1.1 プロバイダ クラスが DataAdapter です。SqlDataAdapter など、プロバイダ固有の基本クラスはすべて DbDataAdapter から派生し、この DbDataAdapter は DataAdapter から派生しています。ADO.NET 2.0では、すべてのデータ クラスが基本クラスから派生します。新しい ADO.NET のプロバイダの基本クラスを、表 2 に表示します。これらのクラスは System.Data.Common で定義されています。ADO.NET 2.0 では、新しい汎用機能がすべて基本クラス (インターフェイスではない) に含まれます。

表 2. ADO.NET 2.0 における汎用基本クラスおよび汎用インターフェイス

SqlClient クラス 基本クラス 汎用インターフェイス
SqlConnection DbConnection IDbConnection
SqlCommand DbCommand IDbCommand
SqlDataReader DbDataReader IDataReader/IDataRecord
SqlTransaction DbTransaction IDbTransaction
SqlParameter DbParameter IDbDataParameter
SqlParameterCollection DbParameterCollection IDataParameterCollection
SqlDataAdapter DbDataAdapter* IDbDataAdapter
SqlCommandBuilder DbCommandBuilder  
SqlConnectionStringBuilder DbConnectionStringBuilder  
SqlPermission DBDataPermission*  

* これらの基本クラスは ADO.NET 1.0 に存在したものです。

これらの "主な" 基本クラスに加え、ADO.NET 2.0 には多くの新しい基本クラスが追加されています。このうちのいくつかについては、この記事の中で後ほど説明します。ただし、ADO.NET のプロバイダ基本クラスは抽象で、これはこれらの基本クラスを直接インスタンス化できないことを意味します。上記のインターフェイスベースのコードは、次のように変わります。

enum provider {sqlserver, oracle, oledb, odbc};
public DbConnection GetConnectionBaseClass()
{
// 構成からプロバイダを決定します
provider prov = GetProviderFromConfigFile();
DbConnection conn = null;
switch (prov) {
  case provider.sqlserver: 
    conn = new SqlConnection(); break;
  case provider.oracle:     
    conn = new OracleConnection(); break;
 // アプリケーションが新しいプロバイダをサポートするため、これらを追加します 
 }
return conn;
}

これは、プログラマの使い勝手のよさという観点からは大した改善ではないので、次はプロバイダ ファクトリについて説明します。

プロバイダ ファクトリ

上記の case ステートメントを利用するより、むしろ "適切な" プロバイダ固有接続のインスタンス化に基づいた DbConnection を提供するクラスを持つ方が便利です。しかし、どうすれば SqlConnectionOracleConnection のどちらをインスタンス化すればよいか分かるのでしょうか。この解決策が、プロバイダ ファクトリ クラスを使用して正しいタイプの具体的なクラスを提供することです。各プロバイダには、SqlClientFactoryOracleClientFactoryOleDbFactory などのプロバイダ ファクトリ クラスが実装されます。これらのクラスはすべて DbProviderFactory から派生し、作成されうるクラスを配布する静的メソッド (Visual Basic .NET で共有) を含みます。ここに、DbProviderFactory メソッドのリストを示します。

表 3. DbProviderFactory メソッド

CreateConnection
CreateCommand
CreateCommandBuilder
CreateConnection
CreateConnectionStringBuilder
CreateDataAdapter
CreateDataSourceEnumerator
CreateParameter
CreatePermission

たとえば、DbDataReader は、"新しい" 演算子により直接作成されないので、CreateDbDataReader クラスは必要ありません。DbDataReader は常に DbCommand.ExecuteReader() を介して作成されます。

DbProviderFactory ベースのクラスを公開する各データ プロバイダは、構成情報を machine.config に登録します。データ プロバイダも web.config のようなプログラム固有の構成ファイルに追加されます。SqlClient データ プロバイダの machine.config の一般的なエントリは次のようになります。

<system.data>
 <DbProviderFactories>
<add name="SqlClient Data Provider" 
 invariant="System.Data.SqlClient" 
 support="FF" 
 description=".Net Framework Data Provider for SqlServer"   
 type="System.Data.SqlClient.SqlClientFactory, System.Data,  
  Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
 <!-- other provider entries elided -->
 </DbProviderFactories>
</system.data>

DbProviderFactories 要素で <add> サブ要素を使用するので、プロバイダを<追加>または**<削除>** して、machine.config をオーバーライドできます。作業していたシステムには SqlServer CE プロバイダがインストールされていなかったため、サンプル コードでは、<remove> を使用してこれを削除しています。サンプルの 1 つには、machine.config レベル、またはアプリケーションや Web 固有の構成ファイルに、追加できるサンプル プロバイダが含まれています。プロバイダを使用するには、プロバイダ自体は Global Assembly Cache (GAC) または DbProviderFactories のアプリケーションの作業ディレクトリにインストールする必要があります。

構成ファイルの属性には、利用可能なデータ プロバイダのリストを "扱いやすい名前" で表示するのに使用されるものもありますが、最も重要な 2 つの属性は、invariantsupport です。Invariant は、次の段落で説明する DbProviderFactories クラスに文字列として渡すことのできる、プロバイダの影響を受けない名前です。サポートは、上記の DbProviderFactory.CreateXXX メソッドを使用して DbProviderFactory クラスが作成することのできるタイプのビットマスクです。

DbProviderFactories クラスは、"ファクトリのためのファクトリ" として考えることができ、プロバイダの選択および、DbProviderFactory のインスタンス可に対応する静的メソッドを持っています。これらを表 4 に表示します。

表 4. DbProviderFactories メソッド

DbProviderFactories メソッド 目的
GetFactoryClasses() machine.config の情報からプロバイダ情報の DataTable を返します。
GetFactory(DataRow) GetFactoryClasses により生成された DataTable からの DataRow を与えられた、適切な DbProviderFactory インスタンスを返します。
GetFactory(string) プロバイダを識別する、プロバイダの影響を受けない名前の文字列を与えられた、適切な DbProviderFactory インスタンスを返します。

DbProviderFactories のメソッドは、.NET データ プロバイダのリストでドロップダウン リスト ボックス を生成する (GetFactoryClasses による) ために使用され、このリストの中からユーザーは 1 つを選択し、GetFactory(DataRow) により適切な DbProviderFactory をインスタンス化します。あるいは単純に、文字列により DbProviderFactory を取得することもできます。そこで、使用するプロバイダをユーザーに確認する場合、プロバイダを選択し、DbConnection をインスタンス化するコードは、次のようになります。

public DbConnection GetConnectionBaseClass2()
{
DbProviderFactory f = GetProviderFactoryFromUserInput();
// ファクトリが DbConnection の作成をサポートする場合は、これを返します
if ((f.SupportedClasses & DbProviderSupportedClasses.DbConnection) > 0)
  return f.CreateConnection();
else
  return null;
}

public DbProviderFactory GetProviderFactoryFromUserInput()
{
DataTable t = DbProviderFactories.GetFactoryClasses();
DataRow r;
// ユーザーが DataTable t から DataRow r を選択します 
r = t.Rows[0]; // 行 0 が選択 DataRow です
return DbProviderFactories.GetFactory(r);
}

いったん適切な DbProviderFactory を取得すれば、作成可能な任意のプロバイダ サポートのクラスをインスタンス化できます。?DbConnection は最も一般的な例です。

これにより、ユーザーはプロバイダを構成できますが、通常これが行われるのは、特定のアプリケーションのセットアップ中に一度だけです。データベースのセットアップおよびデータベースのインスタンスの選択の後、情報はユーザーの構成ファイルに格納されます。これが変更されるのは、会社がデータベース製品を変更するか、データベースのインスタンスの位置が変更になる (たとえば、アプリケーションがテスト サーバーから製品のサーバーに移動する) 場合のみです。ADO.NET 2.0 では組み込み機能によりどちらにも対応するため、独自に実行する必要はありません。

構成情報の指定

構成ファイルの DbProviderFactories セクション以外にも、接続文字列情報を操作する標準の構成ファイル位置および API があります。これには、予想以上の操作が必要となります。実際、OLE DB/ADO、ODBC、および JDBC の接続文字列には 2 つの個別タイプの情報が含まれます。プロバイダに接続する方法についての情報の、プロバイダ/ドライバおよび名前/値のペアを認識する情報です。ここに一般的な従来の ADO の接続文字列を示します。

"provider=sqloledb;data source=mysvr;integrated security=sspi;initial catalog=pubs"

Visual Basic 6.0 のプログラマが次のステートメントをコーディングする場合、次のようになります。

Dim conn as New Connection
conn.Open connstr 'use the connection string defined above

接続文字列情報は、以下の目的で、OLE DB サービス コンポーネントにより使用されます。

  1. 指定したプロバイダ名の OLE DB データ ソース クラスで CoCreateInstance を呼び出すことにより、レジストリを検索し、適切なプロバイダをロードします。
  2. OLE DB データ ソース (およびセッション) オブジェクトをカプセル化する ADO 接続インスタンスを作成します。
  3. 適切な ADO インターフェイス ポインタ (この場合は _Connection ) をプログラムに返します。
  4. ADO _Connection インターフェイスで Open() (OLE DB インターフェイスを使用して、基になる OLE DB プロバイダ コードを呼び出す) を呼び出します。

ADO.NET 接続文字列には、プロバイダ情報は含まれません。プログラマはプログラムのコーディング時に使用するプロバイダを決定する (そして、たとえば SqlConnection を使用する) か、プロバイダ情報を格納するメカニズムを考案する必要があります。ADO.NET 2.0 の接続構成情報では、プロバイダ情報を取り込むことで、これに対応しています。app.config ファイルの一般的な ADO.NET 2.0 接続文字列は、次のようになります。

<configuration>
 <connectionStrings>
   <add name="Publications" providerName="System.Data.SqlClient" 
     connectionString="Data Source=MyServer;Initial Catalog=pubs;
                       integrated security=SSPI" />
 </connectionStrings>
</configuration>

... さらに、接続文字列自体の他に、接続文字列名と プロバイダ名 が含まれます。

この情報は、ConnectionStringSettingsConnectionStringsSettingsCollection コレクション クラスを通じて公開されます。接続文字列を名前で取得することも、ごく小さなコードにより必要な情報すべてを取得することも可能です。上記の接続文字列情報を使用すると、汎用的な DbConnection 取得プログラムは、次のようになります。

public DbConnection GetInitializedConnectionBaseClass()
{
DbConnection conn = null;
ConnectionStringSettings s =    
  ConfigurationSettings.ConnectionStrings["Publications"];
DbProviderFactory f = DbProviderFactories.GetFactory(
  s.ProviderName);

if ((f.SupportedClasses & DbProviderSupportedClasses.DbConnection) > 0)
 {
  conn =  f.CreateConnection();
  conn.ConnectionString = s.ConnectionString;
 }
return conn;
}

ConnectionStringSettingsCollection を使用すると、名前付きの接続文字列情報を格納することは簡単です。.NET の構成クラスは .NET 2.0 Beta 1 では使用できますが、これ以降のバージョンでは、接続文字列のロードおよび保存の機能は、プロバイダ固有の DbConnectionStringBuilder クラスに組み込まれる可能性があります。次のセクションでは、このクラスについて説明します。

データ ソースの列挙

これで、ADO.NET 2.0 を使用し、まったくプロバイダには依存せずに、接続文字列を構成およびロードし、データベースに接続できます。しかし、接続しているデータベースが変更される場合はどうすればよいでしょうか。データベース管理者 (DBA) が、使用中のデータを SQL Server の "Publications" インスタンスから "Bookstore" インスタンスに移動させたため、構成を変更する必要があるとの電子メールを受信したとします。Systems Management Server または別の自動配置を使用して、新しい構成をプッシュするプロセスを自動化することはできますが、ネットワーク上に SQL Server のインスタンスすべてを一覧表示し、自分自身で再構成できるようにするプログラムを記述することもできます。ADO.NET 2.0 では、このために、データ ソース列挙子クラスを導入します。

ADO.NET データ ソース列挙子は、共通の基本クラス (もちろんです)、DbDataSourceEnumerator から派生しています。上述のパターンを使用し、DbProviderFactory を通じて、適切な DbDataSourceEnumerator を取得できます。DbDataSourceEnumerator クラスでは、単一の静的メソッド、GetDataSources を公開します。データベースまたはデータ ソースが変更になる場合、それを変更するコードは次のようになります。

public void ChangeDataSourceOrProvider()
{
// 上の例でこのメソッドを参照します
DbProviderFactory f = GetProviderFactoryFromUserInput();
// ファクトリが DbConnection の作成をサポートする場合は、これを返します
if ((f.SupportedClasses &  
    DbProviderSupportedClasses.DbDataSourceEnumerator) > 0) 
 {
    DbDataSourceEnumerator e = f.CreateDataSourceEnumerator();
    DataTable t = e.GetDataSources();
    // ユーザーがデータ行 r を選択します
    DataRow r = t.Rows[0];
    string dataSource = (string)r["ServerName"];
    if (r[InstanceName] != null)
      dataSource += ("\\" + r["InstanceName"]);
    // このメソッドを以下に定義します
    RewriteConnectionStringAndUpdateConfigFile(f, dataSource);
 } 
else
  Console.WriteLine("Source must be changed manually");
}

これは、最後の RewriteConnectionStringAndUpdateConfigFile メソッドを除けば、とても単純な汎用コードでした。接続文字列は名前と値ペアであるため、文字列操作を行って、正しい値を置き換えるようにする必要があります。また、データベースのインスタンスのみが変更されるのではなく、ユーザー ID や初期カタログ (データベース) など、その他の接続文字列パラメータも変更される場合はどうすればよいでしょうか。このタスクのためには、組み込みクラスを利用すると便利です。ADO.NET 2.0 では、まさにこのようなクラス、ConnectionStringBuilder が提供されています。もちろん、これは汎用的な基本クラス、DbConnectionStringBuilder に基づいています。

接続文字列のビルド

ADO.NET では、他の汎用的な API と同様、接続文字列は名前と値ペアです。ADO.NET では、名前のキーワードの設定方法について、OLE DB の規則に従うことを推奨はしていますが、OLE DB や ODBC と異なり、義務付けてはいません。OleDb および Odbc が、それぞれ独自の規則に従うデータ プロバイダを仲介します。Microsoft OracleClient は OLE DB の規則に従い、OLE DB と ODBC のキーワードが異なる場合は、SqlClient がどちらかのキーワードをサポートします。ここに例を示します。

表 5. 異なるデータ プロバイダでの接続文字列の名前と値

意味 Odbc OleDb SqlClient OracleClient
接続するソース Server Data Source Server または Data Source Server または Data Source
ユーザー UID User ID UID または User ID User ID
パスワード PWD Password PWD または Password Password
Windows ログイン使用の有無 Trusted_Connection Integrated Security Trusted_Connection または Integrated Security Integrated Security
接続するデータベース Database Initial Catalog Database または Initial Catalog N/A
接続プール   OLE DB Services Pooling Pooling

DbConnectionStringBuilder は、プロバイダ固有の接続文字列ビルダのための基本クラスです。ADO.NET のデータ プロバイダが特定の接続文字列パラメータをサポートすることは保証できないため、DbConnectionStringBuilder が名前と値ペアのディクショナリを保持します。ディクショナリからリスト全体を取得するには、GetKeys および GetValues メソッドを含む、通常のコレクション メソッドすべてが利用可能です。DbConnectionStringBuilder は、ShouldSerialize (たとえばユーザーが、パスワードをファイルにシリアル化したくない場合) など、接続文字列バラメータの特定のプロパティをサポートします。汎用的な DbConnectionStringBuilder のインスタンスは、DbProviderFactory クラスを介して取得できます。特定の ConnectionStringBuilder クラスには、接続するデータベース サーバーを参照する DataSource プロパティ という便利なプロパティがあります。一般には、プロパティはキーワードのようにデータ プロバイダ固有です。

上記の RewriteConnectionStringAndUpdateConfigFile メソッドを DbConnectionStringBuilder を使用して実装する場合、次のようになります。

public void RewriteConnectionStringAndUpdateConfigFile(
  DbProviderFactory f, string dataSource)
{
  Configuration config = Configuration.GetExeConfiguration(_progname,    
     ConfigurationUserLevel.None);
  ConnectionStringSettingsCollection cs =  
     config.ConnectionStrings.ConnectionStrings;

  DbConnectionStringBuilder b = f.CreateConnectionStringBuilder();

  b.ConnectionString = cs["Publications"].ConnectionString;
  if (b.ContainsKey("Data Source"))
  {
    b.Remove("Data Source");
    b.Add("Data Source", dataSource);

    // ConfigurationSettings 接続文字列を更新します
    cs["Publications"].ConnectionString = b.ConnectionString;
    config.Update();
  }
}

接続文字列のビルドについて、最後に述べるべきことは、ODBC と OLE DB のどちらでも、接続情報を構成するためのグラフィック エディタが提供されているということです。ODBC エディタは、コントロール パネル アプレット (ODBC Data Sources) に含まれています。OLE DB エディタは、サフィックス .UDL が付いたファイルがダブルクリックされると必ず起動されます。OLE DB UDL エディタは、Visual Studio .NET で .NET データ プロバイダを構成するのに使用され、SqlClient の接続文字列は後で Visual Studio 自体で処理される必要があります。Visual Studio 2005 には、ConnectionStringBuilder および ProviderFactories クラスを使用するグラフィック エディタがあります。これを確認するには、サーバー エクスプローラを使用して Visual Studio 2005 Beta 1 で新しいデータ ソースを構成します。ここには、もはや OLE DB プロバイダではなく、.NET データ プロバイダが表示されます。Beta 1 のリリースでは、このコンポーネントは Visual Studio と密接に連結されており、まだパブリック クラスを介して利用することはできません。

その他の汎用的なコーディングについての考慮事項

ADO.NET 2.0 の共通基本クラスおよびファクトリを使用して、ほとんど完全に汎用的なコードを記述できます。ただし、データベースはその性質により、完全に汎用的ではありません。プログラマは、どのデータベースでも動作し、それぞれのデータベースで同じ実行結果となるプログラムを記述することは、これまでできませんでした。この顕著な例として、SQL Server や Oracle などのクライアントサーバー データベース で成果を上げている方法が、Access や FoxPro などファイルベースのデータ ストアではうまくいかないということがあります。ADO の段階で、ファイルベースのデータ ストアとクライアントサーバー (ネットワーク) データベース間の移植には、プロバイダと接続文字列の変更以上のものが含まれることがわかりました。

データ ソースの違いに対処するため、各データ プロバイダでは、他のデータ プロバイダではサポートされていないプロパティ、メソッド、およびイベントをサポートすることができます。基本クラスの背後にある前提は、基本クラスは汎用的な機能を公開するということです。プロバイダ固有の機能はプロバイダ固有のクラスで公開されます。C# のキャスト構文、"as" または "is"、または Visual Basic .NET の CType メソッドを使用したキャストにより、いつでもこれらの追加機能を利用できます。ここに、C# のコードから SqlConnection固有のメソッド RetrieveStatistics を使用した例を示します。

public void GetStatsIfPossible(DbConnection conn)
{
  if (conn is SqlConnection)
    Hashtable h = ((SqlConnection)conn).RetrieveStatistics();
}

パラメータ化されたクエリまたはストアド プロシージャにおいて、他にプロバイダによって異なる箇所はパラメータの使用法です。ADO.NETにおいて、データ プロバイダは、名前付きパラメータ (パラメータ名が重要で、パラメータが指定される順序は無関係) か、位置指定パラメータ (パラメータの順序が重要で、名前は無関係) のどちらか、あるいはその両方をサポートできます。SqlClient および Microsoft OracleClient プロバイダは名前付きパラメータを使用し、OleDb および Odbc は位置指定パラメータを使用しています。パラメータ化されたクエリを使用している場合、もう 1 つの相違点はパラメータ マーカーです。各プロバイダには、パラメータ マーカーについて独自の概念があります。ここに、例として ADO.NET に同梱されるプロバイダを使用して、相違点についての簡潔な一覧を示します。DataDirectTechnologies または Oracle Corporation からプロバイダを取得する場合、これらは異なります。

表 6. 異なる ADO.NET データ プロバイダにおけるパラメータの使用スタイル

プロバイダ 名前付き/位置指定 パラメータ マーカー
SqlClient 名前付き @parmname
OracleClient 名前付き :parmname (または parmname)
OleDb 位置指定 ?
Odbc 位置指定 ?

その他の違いはデータベースのネットワーク プロトコルの仕組みに基づいています。ネットワーク プロトコルにより、結果のパケットがデータベースからクライアントに返される方法が定義されます。たとえば、SQL Server では TDS (tabular data stream) プロトコルを使用し、Oracle では TNS (transparent network substrate) を使用しています。データベースとプロトコルの仕組みのため、SQL Server 2005 以前のバージョンの SQL Server は、アクティブな結果セット (ADO.NET 用語で DataReader) を一度に 1つだけしか持つことができません。SQL Server 2005 にはこの制限はありません。1 つの接続で複数のアクティブな結果セットを許可する機能は MARS として知られています。SQL Server では、出力パラメータは、結果セットが返され、DataReader が閉じられた後でのみ、利用可能です。一方 Oracle では、ストアド プロシージャから特別なタイプの出力パラメータ REFCURSOR として結果セットを返し、DataReader と出力パラメータに対して異なる動作をします。完全に汎用的なものはありません。

総括

ProviderFactory、DataSourceEnumerator、および ConnectionStringBuilder クラスの導入により、ADO.NET 2.0 では、構成時、あるいは実行時に、簡単にプロバイダ間の切り替えができます。接続情報を構成しやすくするヘルパー クラスがあり、この接続情報は標準的な形式で構成ファイルに格納されます。 System.Data.Common の基本クラスにより、ADO.NET の将来のリリースで新しいインターフェイスを追加することなく、簡単に基本プロバイダ プロファイルに機能を追加できます。私の Web サイト Non-MS link に、基本クラスを使用し、プロバイダ ファクトリを実装する "世界で最も単純な" ADO.NET データ プロバイダの更新バージョンを投稿しました。元のプロバイダについては、筆者の MSDN Magazine の記事、ADO.NET: Building a Custom Data Provider for Use with the .NET Data Access Framework を参照してください。

データベースおよびプロバイダがどのようにデータベース メタデータを公開するかということについて、紹介していないプロバイダ固有の項目が 1 つあります (SQL-92/99 の INFORMATION_SCHEMA)。各データベースでは、テーブル、列、主キーなどについての情報のリストを公開する方法が多少異なります。同じデータベース製品でもバージョンにより異なる場合があります。このシリーズの次の記事では、データベースやバージョンに依存しない方法でメタデータを公開するために ADO.NET 2.0 が提供するメカニズムを見ていきます。では、またお会いしましょう。

Bob Beauchemin 氏は、DevelopMentor の教官、学習コースの作成者、およびデータベース カリキュラム コースの連絡係です。データ中心の分散システムのアーキテクト、プログラマ、および管理者としての 25 年を超える経験を持っています。Microsoft Systems Journal、SQL Server Magazine、およびその他のものに ADO.NET、OLE DB、SQL Server に関する記事を執筆したことがあり、A First Look at SQL Server 2005 for DevelopersEssential ADO.NETの著者でもあります。