連載:Visual Studio 2005でいってみようDBプログラミング

第6回 データソース・コントロールでいってみよう(前編)

山田 祥寛(http://www.wings.msn.to/
2006/07/29
Page1 Page2 Page3

 前回までは、もっぱらSqlDataSourceコントロールを利用したデータベースへのアクセス方法について紹介してきました。なるほど、SqlDataSourceはとても強力なコントロールで、これだけでもかなりの機能を実現することが可能です。

 しかし、アプリケーション構築の局面では、多分にしてイレギュラーな要件が発生します。例えば、条件やページの状態によって発行するSQL命令そのものを動的に変更したい、SqlDataSourceコントロールが対応していないデータソースにアクセスしたい、などのケースです。

 そのような場合には、やはりデータソースへの接続、データの取得、データアクセス・コントロールへのデータの流し込み(バインド)といった操作を一からコーディングしなければならないのでしょうか。いえいえ、そのようなことはありません。ASP.NET 2.0が提供するObjectDataSourceコントロールを利用することで、自作のデータアクセス用クラス(データアクセス・コンポーネント)を経由してデータソース操作を行うことが可能になります。

 ObjectDataSourceコントロールとは自作のクラス・ロジックとGridView/DetailsViewのようなデータアクセス・コントロールとを結び付ける接着剤のようなものである、と思っていただければよいでしょう。


図1 ObjectDateSourceコントロール
ObjectDataSourceコントロールとは自前で作成したデータアクセス用クラス(データアクセス・コンポーネント)とGridView/DetailsViewのようなデータアクセス・コントロールとを結び付ける接着剤のようなものである。

 本稿では、このObjectDataSourceコントロールの基本的な使い方を紹介すると同時に、これまではSqlDataSourceコントロールでブラックボックス化されていたデータ取得のコード作法についても紹介します。

今回作成するサンプル・プログラムのダウンロード(vs2005db_06.zip)

グリッド表の内容をプルダウン・メニューで絞り込む

 本稿では、第3回でも紹介したサンプルを、ObjectDataSourceコントロールを使って書き直してみます。

 第3回のサンプルではプルダウン・メニューで指定された出版社名に従って常に絞り込み表示を行いましたが、ここでは[未選択]という状態を設け、未選択の場合にはテーブル内の全データを表示することにします。


図2 本稿で紹介するサンプルの実行結果
プルダウン・メニューで指定された出版社名に従って絞り込み表示を行うが、未選択の場合にはテーブル内の全データを表示する。

 それではさっそく、具体的な手順を見ていくことにしましょう。

[1]データアクセス・コンポーネントを作成する

 ObjectDataSourceコントロールを利用するには、まずデータソースへのアクセスを担当するデータアクセス・コンポーネント(クラス)を作成する必要があります。

 ソリューション・エクスプローラから「App_Code」フォルダを右クリックし、表示されたコンテキスト・メニューから[新しい項目の追加]を選択してください*1。[新しい項目の追加]ダイアログが表示されますので、表1の要領で入力したうえで、[追加]ボタンをクリックします。

項目 設定値
テンプレート クラス
名前 Book.vb
言語 Visual Basic
表1 [新しい項目の追加]ダイアログの入力項目

*1 第2回でも紹介したように、「App_Code」フォルダは「.vb」「.cs」などのコードを配置するためのアプリケーション・フォルダで、本フォルダの内容は実行時に動的にコンパイルされます。

 コード・エディタが開きますので、リスト1のようにコードを入力してみましょう。

Imports Microsoft.VisualBasic
Imports System.ComponentModel
Imports System.Data
Imports System.Data.Common

Public Class Book

  <DataObjectMethod(DataObjectMethodType.Select, True)> _
  Public Function getBooksDataReader(ByVal publish As String) As DbDataReader

    Dim setting As ConnectionStringSettings = _
      ConfigurationManager.ConnectionStrings("MyDB")

    Dim factory As DbProviderFactory = _
      DbProviderFactories.GetFactory(setting.ProviderName)

    Dim db As DbConnection = factory.CreateConnection()

    db.ConnectionString = setting.ConnectionString
    Dim comm As DbCommand = factory.CreateCommand()
    comm.Connection = db
   
    If publish <> "-" Then
      comm.CommandText = _
        "SELECT * FROM books WHERE publish=@publish"
      Dim param As DbParameter = factory.CreateParameter()
      param.ParameterName = "@publish"
      param.Value = publish
      comm.Parameters.Add(param)
    Else
      comm.CommandText = "SELECT * FROM books"
    End If
   
    db.Open()
    Return comm.ExecuteReader(CommandBehavior.CloseConnection)
  End Function

End Class
リスト1 Book.vb
データアクセス・コンポーネントの内容。

 以前のADO.NET 1.xでは使用するデータ・プロバイダによって、使用するクラスの名前空間をSystem.Data.SqlClient名前空間(SQL Server用)、System.Data.OracleClient名前空間(Oracle用)、System.Data.OleDb名前空間(OLE DBプロバイダ用)、System.Data.Odbc名前空間(ODBCプロバイダ用)などから使い分ける必要がありました。

 しかし、これはコードがデータ・プロバイダに依存する原因ともなりますので、あまり好ましい状態ではありません。例えばバックエンドのデータベース・サーバをOracleからSQL Serverに移行したいといった場合にも、それぞれのデータ・プロバイダに依存するコードをすべて書き換える必要があるためです。

 そこでADO.NET 2.0では、System.Data.Common名前空間に「汎用クラス」が追加されました。具体的にはDbConnection、DbCommand、DbDataReader、DbParameterなどのクラスです。これら汎用クラスを利用することで、使用するデータ・プロバイダに依存しない汎用的なコードを記述することが可能になります。

 汎用クラスを利用するには、まずConfigurationManager.ConnectionStringsプロパティからConnectionStringSettingsオブジェクトを取得します(リスト1- )。

Dim setting As ConnectionStringSettings = _
  ConfigurationManager.ConnectionStrings("MyDB")

 ConnectionStringSettingsオブジェクトは、アプリケーション構成ファイル(Web.config)で設定された単一のデータベース接続情報を表します*2。例えば、そのProviderNameプロパティを介することでWeb.config上のデータ・プロバイダ名を、ConnectionStringプロパティを介することでデータベース接続文字列を、それぞれ取得することが可能です。

*2 リスト1- で指定した“MyDB”は第2回で設定済みの接続名です。データベース接続情報は、データベース構成ウィザードを利用するほか、Web.configを直接編集することでも設定が可能です。

 リスト1- では、DbProviderFactories.GetFactoryメソッドにプロバイダ名を引き渡すことで、DbProviderFactoryオブジェクトを取得しています。

Dim factory As DbProviderFactory = _
  DbProviderFactories.GetFactory(setting.ProviderName)

 DbProviderFactoryオブジェクトは、データ・プロバイダに応じてDbConnection、DbCommandなど汎用クラスの適切なインスタンスを生成するための手段を提供します。例えば、CreateConnectionメソッドを使用することで、DbConnectionオブジェクトを作成することができます(リスト1- )。

Dim db As DbConnection = factory.CreateConnection()

 もっともDbConnectionオブジェクトも、生成された直後は接続を実行するための単なる道具にすぎません。ConnectionStringプロパティでデータベース接続文字列を設定し、Openメソッドを実行するまで、接続そのものは確立されませんので注意してください*3

*3 データベースへの接続は、コマンドやパラメータの設定など、すべての準備が完了してから確立するのが一般的です。実際には接続後に諸設定を行ってもさほどのオーバーヘッドにはなりませんが、接続を占有する時間をできるだけ短くする癖をつけるようにしておくのは決して誤りではありません。

[コラム]個別のデータ・プロバイダを取得する

 コードの可搬性という観点からは、汎用クラスの利用は有効なアプローチです。しかし時として、データ・プロバイダ固有の機能に依存するメソッドを利用したいというケースもあるはずです。そのような場合には、以下のようにデータ・プロバイダ固有のクラスにキャストすることで、プロバイダ固有のメソッドにアクセスすることが可能になります。

Dim sqldb As SqlConnection = DirectCast(db ,SqlConnection)
DbConnectionオブジェクト(db)のSqlConnectionオブジェクトへのキャスト
DbConnectionクラスは汎用クラスであるが、ここではそれをSQL Server用のクラスであるSqlConnectionクラスのオブジェクトにキャストしている(もちろんここではDBMSとしてSQL Serverを利用しているものとする)。これによりSQL Server固有の機能にアクセスできる。

 データベースに対してSQL命令を発行するのは、DbCommandオブジェクトの役割です。DbCommandオブジェクトは、DbConnectionオブジェクトと同様、DbProviderFactoryオブジェクトを介してCreateCommandメソッドを呼び出すことで取得できます。

 ここでは、生成したDbCommandオブジェクトに対して、CommandTextプロパティで発行するSQL命令と、ConnectionプロパティでSQL実行に使用する接続を、それぞれ設定しておくことにしましょう。

 CommandTextプロパティには「@」で始まるパラメータのプレイスホルダを指定することが可能です(リスト1- )。

comm.CommandText = _
  "SELECT * FROM books WHERE publish=@publish"

 プレイスホルダとは、その名のとおり、パラメータ値を後から動的に埋め込むための「置き場所」です。ここでは、パラメータとして出版社名が与えられた場合には、これをキーにbooksテーブルを絞り込み検索し、出版社名が空(この場合メソッドのpublishパラメータは「-」となっている)である場合にはbooksテーブルを全件取得しているというわけです。

 コマンド(DbCommandオブジェクト)中のプレイスホルダにパラメータを渡すには、DbParameterオブジェクトを使用します。DbParameterオブジェクトは、DbConnection/DbCommandオブジェクトと同様、DbProviderFactoryオブジェクトからCreateParameterメソッドで生成します。

 パラメータ名(ParameterNameプロパティ)、パラメータ値(Valueプロパティ)を設定した後、DbParameterCollectionオブジェクトのAddメソッドでコマンドに追加してください。DbParameterCollectionオブジェクトは、DbCommandオブジェクトのParametersプロパティから取得できます(リスト1- )。

comm.Parameters.Add(param)

 以上でSQL命令の準備は完了です。

 あとはDbCommandオブジェクトのExecuteReaderメソッドを使用してSQL命令をデータベース・サーバに対して発行します。このメソッドはSQL命令を実行するとともに、データベースから得た結果をDbDataReaderオブジェクトとして返します。DbDataReaderオブジェクトは、取得した結果セットの内容を保持するとともに、これを先頭から順に読み込むための手段を提供します(リスト1- )。

comm.ExecuteReader(CommandBehavior.CloseConnection)

 なお、ExecuteReaderメソッドのパラメータにはコマンドの付随的なオプションをCommandBehavior列挙体(System.Data名前空間)のメンバで指定することができます。CommandBehavior.CloseConnectionが指定された場合、DbDataReaderオブジェクトが破棄されたタイミングで、それに関連付いた接続(DbConnectionオブジェクト)も自動的に破棄されます。

[コラム]App_Codeフォルダに複数言語で作成したクラスを混在させるには?

 App_Codeフォルダには異なる言語(例えばVisual BasicとC#)で記述したクラス・ファイルを混在させて配置することはできません。通常、1つのアプリケーションでは使用する言語も統一することをお勧めしますが、何らかの理由でそのような必要が生じた場合には、使用する言語単位でフォルダを分割してください。

 例えば、App_Codeフォルダの直下にはVisual Basicで記述されたクラス・ファイルを、App_Code/csharpフォルダにはC#のクラスを、というようにします。そのうえで、アプリケーション構成ファイル(Web.config)に以下の記述を追加してください。

<system.web>
  <compilation>
    <codeSubDirectories>
      <add directoryName="csharp"/>
    </codeSubDirectories>
  </compilation>
</system.web>
リスト App_Codeフォルダに複数言語で作成したクラスを作成する場合のWeb.configの設定例

 directoryName属性にはApp_Codeフォルダからの相対パスを指定してください。これによって、「App_Code」フォルダには異なる言語で記述したクラス・ファイルを混在させることが可能になります。


 INDEX
  Visual Studio 2005でいってみようDBプログラミング
  第6回 データソース・コントロールでいってみよう(前編)
  1.データアクセス・コンポーネントを作成する
    2.ObjectDataSourceコントロールを設定する
    3.ObjectDataSourceコントロールに削除機能を実装する
 
インデックス・ページヘ  「Visual Studio 2005でいってみようDBプログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間