このチュートリアルでは、まず型指定されたデータセットを使用してデータ アクセス層 (DAL) を作成し、データベース内の情報にアクセスします。
はじめに
Web 開発者の作業の中心は、データを操作することです。 データを保存するためのデータベース、データを取得して変更するためのコード、データを収集して集計するための Web ページを作成します。 これは、ASP.NET 2.0 でこれらの一般的なパターンを実装するための手法について説明する、長いシリーズの最初のチュートリアルになります。 まず、Typed DataSets を使用したデータ アクセス層 (DAL) で構成される ソフトウェア アーキテクチャ 、カスタム ビジネス ルールを適用するビジネス ロジック層 (BLL)、共通のページ レイアウトを共有する ASP.NET ページで構成されるプレゼンテーション レイヤーを作成します。 このバックエンドの下準備が完了したら、レポートに進み、Web アプリケーションからデータを表示、集計、収集、検証する方法を示します。 これらのチュートリアルは簡潔で、プロセスを視覚的に説明するためのスクリーン ショットを豊富に含む詳細な手順を説明することを目的としています。 チュートリアルにはそれぞれ C# と Visual Basic のバージョンが用意されており、使用される完全なコードのダウンロードが含まれています (この最初のチュートリアルは非常に長いですが、残りのチュートリアルはより消化しやすい短いセクションで提供されています)。
これらのチュートリアルでは、App_Data
ディレクトリに配置された Northwind データベースの Microsoft SQL Server 2005 Express Edition バージョンを使用します。
App_Data
フォルダーには、データベース ファイルのほか、別のデータベース バージョンを使用する場合に備えて、データベースを作成するための SQL スクリプトも含まれています。 Northwind データベースの別の SQL Server バージョンを使用する場合は、NORTHWNDConnectionString
ファイルの Web.config
設定を更新する必要があります。 Web アプリケーションは、Visual Studio 2005 Professional Edition を使用して、ファイル システムベースの Web サイトプロジェクトとして構築されました。 ただし、すべてのチュートリアルは、Visual Studio 2005 の無料版 Visual Web Developer でも同様に機能します。
このチュートリアルでは、最初からデータ アクセス層 (DAL) を作成し、次に 2 番目のチュートリアルで ビジネス ロジック レイヤー (BLL) を作成し、3 番目のチュートリアルで ページ レイアウトとナビゲーション に取り組みます。 4 番目以降のチュートリアルは、最初の 3 つのチュートリアルで作成された基礎に基づいています。 この最初のチュートリアルで取り上げる必要がある内容が多いため、Visual Studio を起動して始めましょう。
手順 1: Web プロジェクトの作成とデータベースへの接続
データ アクセス層 (DAL) を作成する前に、まず Web サイトを作成し、データベースをセットアップする必要があります。 まず、新しいファイル システムベースの ASP.NET Web サイトを作成します。 これを行うには、[ファイル] メニューに移動し、[新しい Web サイト] を選択して、[新しい Web サイト] ダイアログ ボックスを表示します。 ASP.NET Web サイト テンプレートを選択し、[場所] ドロップダウン リストを [ファイル システム] に設定し、Web サイトを配置するフォルダーを選択して、言語を Visual Basic に設定します。
図 1: Web サイト System-Based 新しいファイルを作成する (フルサイズの画像を表示する をクリックします)
これにより、Default.aspx
ASP.NET ページ、App_Data
フォルダー、Web.config
ファイルを含む新しい Web サイトが作成されます。
Web サイトを作成したら、次の手順として、Visual Studio のサーバー エクスプローラーでデータベースへの参照を追加します。 サーバー エクスプローラーにデータベースを追加すると、Visual Studio 内からテーブル、ストアド プロシージャ、ビューなどを追加できます。 テーブル データを表示したり、手動またはクエリ ビルダーを使用してグラフィカルに独自のクエリを作成したりすることもできます。 さらに、DAL の型指定されたデータセットを作成する場合、Visual Studio が型指定されたデータセットを構築する際の元になるデータベースをポイントする必要があります。 その時点でこの接続情報を指定できますが、Visual Studio によって、サーバー エクスプローラーに既に登録されているデータベースのドロップダウン リストが自動的に設定されます。
サーバー エクスプローラーに Northwind データベースを追加する手順は、App_Data
フォルダー内の SQL Server 2005 Express Edition データベースを使用するか、代わりに使用する Microsoft SQL Server 2000 または 2005 データベース サーバーのセットアップが存在するかによって異なります。
App_Data
フォルダー内のデータベースの使用
接続する SQL Server 2000 または 2005 データベース サーバーが存在しない場合、または単にデータベースをデータベース サーバーに追加する必要がない場合は、ダウンロードした Web サイトの App_Data
フォルダー (NORTHWND.MDF
) に配置されている SQL Server 2005 Express Edition バージョンの Northwind データベースを使用できます。
App_Data
フォルダーに配置されたデータベースは、サーバー エクスプローラーに自動的に追加されます。 コンピューターに SQL Server 2005 Express Edition がインストールされていると仮定すると、サーバー エクスプローラーに NORTHWND.MDF という名前のノードが表示され、これを展開してテーブル、ビュー、ストアド プロシージャなどを参照できます (図 2 を参照)。
App_Data
フォルダーには、Microsoft Access の .mdb
ファイルを格納することもできます。このファイルは、対応する SQL Server と同様に、サーバー エクスプローラーに自動的に追加されます。 SQL Server オプションを使用しない場合は、 常に Northwind Traders データベースとアプリをインストール し、 App_Data
ディレクトリにドロップできます。 ただし、Access データベースは SQL Server ほど機能が充実しておらず、Web サイトのシナリオで使用されるように設計されていないことに注意してください。 さらに、35 以上のチュートリアルのうちのいくつかでは、Access ではサポートされていない特定のデータベースレベルの機能を使用します。
Microsoft SQL Server 2000 または 2005 データベース サーバーのデータベースへの接続
または、データベース サーバーにインストールされている Northwind データベースに接続することもできます。 データベース サーバーに Northwind データベースがまだインストールされていない場合は、まず、このチュートリアルのダウンロードに含まれるインストール スクリプトを実行して、データベース サーバーに追加する必要があります。
データベースをインストールしたら、Visual Studio のサーバー エクスプローラーに移動し、[データ接続] ノードを右クリックして、[接続の追加] を選択します。 サーバー エクスプローラーが表示されない場合は、[表示]/[サーバー エクスプローラー] に移動するか、Ctrl + Alt + S キーを押します。 これにより、[接続の追加] ダイアログ ボックスが表示され、接続先のサーバー、認証情報、データベース名を指定できます。 データベース接続情報を正常に構成し、[OK] ボタンをクリックすると、データベースが [データ接続] ノードの下にノードとして追加されます。 データベース ノードを展開して、テーブル、ビュー、ストアド プロシージャなどを調べることができます。
図 2: データベース サーバーの Northwind データベースへの接続を追加する
手順 2: データ アクセス層の作成
データを操作する際、データ固有のロジックをプレゼンテーション層に直接埋め込むこともできます (Web アプリケーションでは、ASP.NET ページがプレゼンテーション層を構成します)。 これは、ASP.NET ページのコード部分に ADO.NET コードを記述するか、マークアップ部分から SqlDataSource コントロールを使用する形式をとる場合があります。 どちらの場合も、この方法では、データ アクセス ロジックとプレゼンテーション層が密結合されます。 ただし、おすすめの方法は、データ アクセス ロジックをプレゼンテーション層から分離することです。 この分離された層はデータ アクセス層 (DAL) と呼ばれ、通常は別のクラス ライブラリ プロジェクトとして実装されます。 この階層化アーキテクチャのメリットは適切に文書化されており (これらの利点については、このチュートリアルの最後にある「参考資料」セクションを参照してください)、このシリーズではこのアプローチを使用します。
データベースへの接続の作成、SELECT
、INSERT
、UPDATE
、DELETE
コマンドの発行など、基になるデータ ソースに固有のすべてのコードは DAL に配置する必要があります。 プレゼンテーション層には、このようなデータ アクセス コードへの参照を含めず、代わりに、すべてのデータ要求に対して DAL を呼び出す必要があります。 データ アクセス層には、通常、基になるデータベース データにアクセスするためのメソッドが含まれています。 たとえば、Northwind データベースには、販売する製品とそれらが属するカテゴリを記録する Products
テーブルおよび Categories
テーブルがあります。 DAL には、次のようなメソッドが用意されています。
-
GetCategories(),
は、すべてのカテゴリに関する情報を返します -
GetProducts()
は、すべての製品に関する情報を返します -
GetProductsByCategoryID(categoryID)
は、指定されたカテゴリに属するすべての製品を返します -
GetProductByProductID(productID)
は、特定の製品に関する情報を返します
これらのメソッドを呼び出すと、データベースに接続されて適切なクエリが発行され、結果が返されます。 結果が返される方法は重要です。 これらのメソッドは、データベース クエリによって設定された DataSet または DataReader を単に返すことができますが、 厳密に型指定されたオブジェクトを使用してこれらの結果を返すのが理想的です。 厳密に型指定されたオブジェクトは、コンパイル時にスキーマが厳密に定義される一方、弱く型指定されたオブジェクトは実行時までスキーマが不明です。
たとえば、DataReader とデータセット (既定) は、データの設定に使用されるデータベース クエリが返す列によってスキーマが定義されるため、弱く型指定されたオブジェクトです。 弱く型指定された DataTable から特定の列にアクセスするには、次のような構文を使用する必要があります。DataTable.Rows(index)("columnName")
この例では、文字列または序数インデックスを使用して列名にアクセスする必要があるため、DataTable が弱く型指定されていることを示しています。 一方、厳密に型指定された DataTable では、各列がプロパティとして実装されており、次のようなコードになります。DataTable.Rows(index).columnName
厳密に型指定されたオブジェクトを返すには、開発者が独自のカスタム ビジネス オブジェクトを作成するか、型指定されたデータセットを使用します。 ビジネス オブジェクトは、そのプロパティが通常、ビジネス オブジェクトが表す基になるデータベース テーブルの列を反映するクラスとして、開発者によって実装されます。 型指定されたデータセットは、データベース スキーマに基づいて Visual Studio によって生成され、そのメンバーがこのスキーマに従って厳密に型指定されたクラスです。 型指定されたデータセット自体は、ADO.NET データセット、DataTable、および DataRow クラスを拡張するクラスで構成されます。 型指定されたデータセットには、厳密に型指定された DataTable に加えて、データセットの DataTable にデータを設定し、DataTable 内の変更をデータベースに反映するためのメソッドを持つクラスである TableAdapter も含まれるようになりました。
注
型指定されたデータセットとカスタム ビジネス オブジェクトを使用する場合の長所と短所の詳細については、「 データ層コンポーネントの設計と階層を介したデータの受け渡し」を参照してください。
これらのチュートリアルのアーキテクチャでは、厳密に型指定されたデータセットを使用します。 図 3 は、型指定されたデータセットを使用するアプリケーションのさまざまな層間のワークフローを示しています。
図 3: すべてのデータ アクセス コードが DAL に委任されます (フルサイズの画像を表示する をクリックします)。
型指定されたデータセットと Table Adapter の作成
DAL の作成を開始するには、まず、型指定されたデータセットをプロジェクトに追加します。 これを行うには、ソリューション エクスプローラーでプロジェクト ノードを右クリックし、[新しい項目の追加] を選択します。 テンプレートの一覧から DataSet オプションを選択し、Northwind.xsd
という名前を付けます。
図 4: プロジェクトに新しいデータセットを追加することを選択します (フルサイズの画像を表示する をクリックします)
[追加] をクリックした後、DataSet を App_Code
フォルダーに追加するように求められたら、[はい] を選択します。 その後、型指定されたデータセットのデザイナーが表示され、TableAdapter 構成ウィザードが起動し、最初の TableAdapter を型指定されたデータセットに追加できます。
型指定されたデータセットは、厳密に型指定されたデータのコレクションとして機能します。厳密に型指定された DataTable インスタンスで構成され、それぞれが厳密に型指定された DataRow インスタンスで構成されます。 このチュートリアル シリーズで使用する基になるデータベース テーブルごとに、厳密に型指定された DataTable を作成します。 まず、Products
テーブルの DataTable を作成しましょう。
厳密に型指定された DataTable には、基になるデータベース テーブルからデータにアクセスする方法に関する情報は含まれていないことに注意してください。 DataTable に設定するデータを取得するには、データ アクセス層として機能する TableAdapter クラスを使用します。
Products
DataTable の場合、TableAdapter には、プレゼンテーション レイヤーから呼び出す GetProducts()
、GetProductByCategoryID(categoryID)
メソッドなどが含まれます。 DataTable は、レイヤー間でデータを渡すために使用される厳密に型指定されたオブジェクトとしての役割を果たします。
TableAdapter 構成ウィザードでは、まず、作業するデータベースの選択を求められます。 ドロップダウン リストには、サーバー エクスプローラー内の該当するデータベースが表示されます。 Northwind データベースをサーバー エクスプローラーに追加していない場合は、この時点で [新しい接続] ボタンをクリックして追加できます。
図 5: Drop-Down リストから Northwind データベースを選択する (フルサイズの画像を表示する をクリックします)
データベースを選択して [次へ] をクリックすると、接続文字列を Web.config
ファイルに保存するかどうかを確認するメッセージが表示されます。 接続文字列を保存すると、TableAdapter クラスでハード コーディングされるのを防ぐことができます。これにより、今後接続文字列情報が変更された場合の作業が簡略化されます。 接続文字列を構成ファイルに保存する場合は、<connectionStrings>
セクションに配置され、IIS GUI 管理ツール内の新しい ASP.NET 2.0 プロパティ ページを使用し、必要に応じて暗号化してセキュリティを強化する、または後で変更できます。この機能は、管理者にとってより理想的です。
図 6: 接続文字列を Web.config
に保存する (フルサイズの画像を表示する をクリックします)
次に、最初の厳密に型指定された DataTable のスキーマを定義し、厳密に型指定されたデータセットにデータを設定するときに TableAdapter で使用する最初のメソッドを指定する必要があります。 これら 2 つの手順は、DataTable に反映するテーブルの列を返すクエリを作成することによって同時に実行されます。 ウィザードの最後で、このクエリにメソッド名を付けます。 この操作が完了したら、このメソッドをプレゼンテーション層から呼び出すことができます。 このメソッドは、定義されたクエリを実行し、厳密に型指定された DataTable を設定します。
SQL クエリの定義を開始するには、まず TableAdapter でクエリを発行する方法を指定する必要があります。 アドホック SQL ステートメントの使用、新しいストアド プロシージャの作成、または既存のストアド プロシージャの使用を行うことができます。 これらのチュートリアルでは、アドホック SQL ステートメントを使用します。
図 7: アドホック SQL ステートメントを使用してデータのクエリを実行する (フルサイズの画像を表示する をクリックします)
この時点で、手動で SQL クエリを入力できます。 TableAdapter で最初のメソッドを作成するときは、通常、対応する DataTable で表現する必要がある列をクエリから返すようにします。 これを実現するには、Products
テーブルのすべての列とすべての行を返すクエリを作成します。
図 8: テキスト ボックスに SQL クエリを入力します (フルサイズの画像を表示する をクリックします)
または、図 9 に示すように、クエリ ビルダーを使用してクエリをグラフィカルに構築します。
図 9: クエリ エディターを使用してクエリをグラフィカルに作成する (フルサイズの画像を表示する をクリックします)
クエリを作成したら、次の画面に進む前に [詳細オプション] ボタンをクリックします。 Web サイト プロジェクトでは、既定で選択されている唯一の詳細オプションは [INSERT、UPDATE、および DELETE ステートメントの生成] です。クラス ライブラリまたは Windows プロジェクトからこのウィザードを実行する場合は、[オプティミスティック同時実行制御の使用] オプションも選択されます。 この時点では、[オプティミスティック同時実行制御の使用] オプションはオフのままにします。 オプティミスティック同時実行制御については、今後のチュートリアルで確認します。
図 10: 挿入、更新、および削除ステートメントの生成オプションのみを選択します (フルサイズの画像を表示する をクリックします)。
詳細オプションを確認したら、[次へ] をクリックして最後の画面に進みます。 ここで、TableAdapter に追加するメソッドを選択するように求められます。 データを設定するには、次の 2 つのパターンがあります。
-
DataTable を パラメーターとして受け取り、クエリの結果に基づいてデータテーブルを設定するメソッドを作成し、この方法で DataTable に入力します。 たとえば、ADO.NET DataAdapter クラスは
Fill()
メソッドでこのパターンを実装します。 - この方法で DataTable を返します。このメソッドは DataTable を自動的に作成して入力し、メソッドの戻り値として返します。
TableAdapter で、これらのパターンの一方または両方を実装することができます。 ここで説明したメソッドの名前を変更することもできます。 これらのチュートリアル全体を通じて後者のパターンのみを使用する場合でも、両方のチェックボックスをオンのままにしておきましょう。 また、ジェネリック GetData
メソッドの名前を GetProducts
に変更します。
オンにすると、最後のチェックボックス "GenerateDBDirectMethods" で、TableAdapter の Insert()
、Update()
、Delete()
メソッドが作成されます。 このオプションをオフのままにした場合は、TableAdapter の唯一の Update()
メソッドを使用してすべての更新を実行する必要があります。このメソッドは、型指定された DataSet、DataTable、単一の DataRow、または DataRows の配列を受け取ります。 (図 9 の詳細プロパティの [INSERT、UPDATE、および DELETE ステートメントの生成] オプションをオフにした場合、このチェックボックスの設定は無効になります)。このチェックボックスはオンのままにしておきましょう。
図 11: メソッド名を GetData
から GetProducts
に変更する (フルサイズの画像を表示する をクリックします)
[完了] をクリックしてウィザードを終了します。 ウィザードが閉じると、先ほど作成した DataTable を表示するデータセット デザイナーに戻ります。
Products
DataTable (ProductID
、ProductName
など) の列の一覧と、ProductsTableAdapter
のメソッド (Fill()
、GetProducts()
) を確認できます。
図 12: Products
DataTable と ProductsTableAdapter
が型指定された DataSet に追加されました (フルサイズの画像を表示する をクリックします)。
この時点で、単一の DataTable (Northwind.Products
) と、厳密に型指定された DataAdapter クラス (NorthwindTableAdapters.ProductsTableAdapter
) と GetProducts()
メソッドを持つ型指定された DataSet があります。 これらのオブジェクトを使用して、次のようなコードからすべての製品の一覧にアクセスできます。
Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
Dim products as Northwind.ProductsDataTable
products = productsAdapter.GetProducts()
For Each productRow As Northwind.ProductsRow In products
Response.Write("Product: " & productRow.ProductName & "<br />")
Next
このコードでは、データアクセス固有のコードを 1 ビットも記述する必要はありませんでした。 ADO.NET クラスのインスタンス作成や、接続文字列、SQL クエリ、ストアド プロシージャへの参照は必要ありませんでした。 代わりに、TableAdapter はローレベルのデータ アクセス コードを提供します。
この例で使用される各オブジェクトも厳密に型指定されているため、Visual Studio では IntelliSense とコンパイル時の型チェックを提供できます。 とりわけ、TableAdapter によって返される DataTable は、GridView、DetailsView、DropDownList、CheckBoxList などの ASP.NET データ Web コントロールにバインドできます。 次の例は、GetProducts()
イベント ハンドラー内でわずか 3 行のコードを使用して、Page_Load
メソッドによって返される DataTable を GridView にバインドする方法を示しています。
AllProducts.aspx
<%@ Page Language="VB" AutoEventWireup="true" CodeFile="AllProducts.aspx.vb"
Inherits="AllProducts" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>View All Products in a GridView</title>
<link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>
All Products</h1>
<p>
<asp:GridView ID="GridView1" runat="server"
CssClass="DataWebControlStyle">
<HeaderStyle CssClass="HeaderStyle" />
<AlternatingRowStyle CssClass="AlternatingRowStyle" />
</asp:GridView>
</p>
</div>
</form>
</body>
</html>
AllProducts.aspx.vb
Imports NorthwindTableAdapters
Partial Class AllProducts
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load
Dim productsAdapter As New ProductsTableAdapter
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
End Sub
End Class
図 13: 製品の一覧が GridView に表示されます (フルサイズの画像を表示する をクリックします)。
この例では、ASP.NET ページの Page_Load
イベント ハンドラーに 3 行のコードを記述する必要がありましたが、今後のチュートリアルでは、ObjectDataSource を使用して DAL からデータを宣言的に取得する方法について検討します。 ObjectDataSource を使用すると、コードを一切記述する必要がなく、ページングと並べ替えもサポートされます。
手順 3: パラメーター化されたメソッドをデータ アクセス層に追加
この時点で、ProductsTableAdapter
クラスには 1 つのメソッドがありますが、GetProducts()
はデータベース内のすべての製品を返します。 すべての製品を操作できることは確かに便利ですが、特定の製品または特定のカテゴリに属するすべての製品に関する情報を取得する必要がある場合もあります。 このような機能をデータ アクセス層に追加するには、パラメーター化されたメソッドを TableAdapter に追加します。
GetProductsByCategoryID(categoryID)
メソッドを追加してみましょう。 DAL に新しいメソッドを追加するには、DataSet デザイナーに戻り、ProductsTableAdapter
セクションを右クリックして [クエリの追加] を選択します。
図 14: TableAdapter での Right-Click とクエリの追加の選択
最初に、アドホック SQL ステートメントを使用してデータベースにアクセスするか、新規または既存のストアド プロシージャを使用するかを確認するメッセージが表示されます。 もう一度、アドホック SQL ステートメントを使用してみましょう。 次に、使用する SQL クエリの種類を尋ねられます。 指定したカテゴリに属するすべての製品を返す必要があるため、行を返す SELECT
ステートメントを記述します。
図 15: 行を返す SELECT
ステートメントを作成することを選択します (フルサイズの画像を表示する をクリックします)。
次の手順では、データへのアクセスに使用する SQL クエリを定義します。 特定のカテゴリに属する製品のみを返す必要があるため、SELECT
から同じ GetProducts()
ステートメントを使用しますが、次の WHERE
句を追加します: WHERE CategoryID = @CategoryID
。
@CategoryID
パラメーターは、作成中のメソッドが対応する型の入力パラメーター (つまり Null 許容整数) を必要とすることを TableAdapter ウィザードに示します。
図 16: 指定したカテゴリの製品のみを返すクエリを入力します (フルサイズの画像を表示する をクリックします)。
最後の手順では、使用するデータ アクセス パターンを選択し、生成されるメソッドの名前をカスタマイズできます。 Fill パターンの場合、戻り値の DataTable 戻りパターン (FillByCategoryID
メソッド) の名前を GetX
に変更し、GetProductsByCategoryID
を使用してみましょう。
図 17: TableAdapter メソッドの名前を選択します (フルサイズの画像を表示する をクリックします)。
ウィザードが完了すると、データセット デザイナーに新しい TableAdapter メソッドが含まれます。
図 18: 製品をカテゴリ別に照会できるようになりました
少し時間を取って、同じ手法を使用して GetProductByProductID(productID)
メソッドを追加します。
これらのパラメーター化されたクエリは、データセット デザイナーから直接テストできます。 TableAdapter でメソッドを右クリックし、[データのプレビュー] を選択します。 次に、パラメーターに使用する値を入力し、[プレビュー] をクリックします。
図 19: 飲料カテゴリに属する製品が表示されます (フルサイズの画像を表示する をクリックします)。
DAL の GetProductsByCategoryID(categoryID)
メソッドを使用して、指定したカテゴリの製品のみを表示する ASP.NET ページを作成できるようになりました。 次の例は、CategoryID
が 1 の Beverages カテゴリに含まれるすべての製品を示しています。
Beverages.aspx
<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Beverages.aspx.vb"
Inherits="Beverages" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>Beverages</h1>
<p>
<asp:GridView ID="GridView1" runat="server"
CssClass="DataWebControlStyle">
<HeaderStyle CssClass="HeaderStyle" />
<AlternatingRowStyle CssClass="AlternatingRowStyle" />
</asp:GridView>
</p>
</div>
</form>
</body>
</html>
Beverages.aspx.vb
Imports NorthwindTableAdapters
Partial Class Beverages
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load
Dim productsAdapter As New ProductsTableAdapter
GridView1.DataSource =
productsAdapter.GetProductsByCategoryID(1)
GridView1.DataBind()
End Sub
End Class
図 20: 飲料カテゴリの製品が表示されます (フルサイズの画像を表示する をクリックします)。
手順 4: データの挿入、更新、削除
データの挿入、更新、削除に一般的に使用されるパターンが 2 つあります。 最初に説明するデータベース ダイレクト パターンでは、呼び出されたときに 1 つのデータベース レコードを操作する INSERT
、UPDATE
、または DELETE
コマンドをデータベースに発行するメソッドの作成が含まれます。 このようなメソッドは通常、挿入、更新、または削除する値に対応する一連のスカラー値 (整数、文字列、ブール値、DateTimes など) で渡されます。 たとえば、Products
テーブルのこのパターンを使用すると、削除メソッドは削除するレコードの ProductID
を示す整数パラメーターを受け取り、挿入メソッドは ProductName
の文字列、UnitPrice
の 10 進数、UnitsOnStock
の整数などを受け取ります。
図 21: 各挿入、更新、および削除要求がデータベースにすぐに送信されます (フルサイズの画像を表示する をクリックします)。
もう 1 つのパターンであるバッチ更新パターンでは、データセット、DataTable、または DataRow のコレクション全体を 1 つのメソッド呼び出しで更新します。 このパターンでは、開発者は DataTable 内の DataRows を削除、挿入、変更してから、それらの DataRows または DataTable を更新メソッドに渡します。 このメソッドは、渡された DataRow を列挙し、(DataRow の RowState プロパティ 値を使用して) 変更、追加、または削除されたかどうかを判断し、各レコードに対して適切なデータベース要求を発行します。
図 22: Update メソッドが呼び出されたときに、すべての変更がデータベースと同期されます (フルサイズの画像を表示する をクリックします)。
TableAdapter は既定でバッチ更新パターンを使用しますが、DB ダイレクト パターンもサポートしています。 TableAdapter の作成時に [詳細プロパティ] から [INSERT、UPDATE、および DELETE ステートメントの生成] オプションを選択したため、ProductsTableAdapter
にはバッチ更新パターンを実装する Update()
メソッドが含まれています。 具体的には、TableAdapter には、型指定された DataSet、厳密に型指定された DataTable、または 1 つ以上の DataRows を渡すことができる Update()
メソッドが含まれています。 TableAdapter を最初に作成したときに [GenerateDBDirectMethods] チェックボックスをオンのままにした場合、Insert()
、Update()
、Delete()
メソッドを使用して DB ダイレクト パターンも実装されます。
どちらのデータ変更パターンでも、TableAdapter の InsertCommand
、UpdateCommand
、DeleteCommand
プロパティを使用して、データベースに対して、INSERT
、UPDATE
、DELETE
コマンドを発行します。 DataSet デザイナーで TableAdapter をクリックし、プロパティ ウィンドウに移動すると、InsertCommand
、UpdateCommand
、DeleteCommand
プロパティを調べて変更できます。 (TableAdapter を選択していること、および ProductsTableAdapter
オブジェクトがプロパティ ウィンドウのドロップダウン リストで選択されていることを確認します。)
図 23: TableAdapter には、 InsertCommand
、 UpdateCommand
、および DeleteCommand
のプロパティがあります (フルサイズの画像を表示する をクリックします)。
これらのデータベース コマンド プロパティのいずれかを調べるか変更するには、クエリ ビルダーを開く CommandText
サブプロパティをクリックします。
図 24: クエリ ビルダーで INSERT
、 UPDATE
、および DELETE
ステートメントを構成する (フルサイズの画像を表示する をクリックします)。
次のコード例は、バッチ更新パターンを使用して、生産中止されておらず、在庫が 25 ユニット以下のすべての製品の価格を 2 倍にする方法を示しています。
Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
Dim products As Northwind.ProductsDataTable = productsAdapter.GetProducts()
For Each product As Northwind.ProductsRow In products
If Not product.Discontinued AndAlso product.UnitsInStock <= 25 Then
product.UnitPrice *= 2
End if
Next
productsAdapter.Update(products)
次のコードは、DB ダイレクト パターンを使用してプログラムによって特定の製品を削除し、更新してから新しい製品を追加する方法を示しています。
Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
productsAdapter.Delete(3)
productsAdapter.Update( _
"Chai", 1, 1, "10 boxes x 20 bags", 18.0, 39, 15, 10, false, 1)
productsAdapter.Insert( _
"New Product", 1, 1, "12 tins per carton", 14.95, 15, 0, 10, false)
カスタムの挿入、更新、削除メソッドの作成
DB ダイレクト メソッドで作成される Insert()
、Update()
、Delete()
メソッドは、特に列数の多いテーブルでは少し面倒な場合があります。 前のコード例を見ると、IntelliSense を使用しないと、Products
テーブルのどの列が Update()
、Insert()
メソッドへの各入力パラメーターにマップされているかが明確ではありません。 1 つまたは 2 つの列のみを更新する場合や、新しく挿入されたレコードの Insert()
(自動増分) フィールドの値を返す、カスタマイズされた IDENTITY
メソッドが必要な場合があります。
このようなカスタム メソッドを作成するには、データセット デザイナーに戻ります。 TableAdapter を右クリックし、[クエリの追加] を選択し、TableAdapter ウィザードに戻ります。 2 番目の画面では、作成するクエリの種類を示すことができます。 新しい製品を追加し、新しく追加されたレコードの ProductID
の値を返すメソッドを作成してみましょう。 このため、INSERT
クエリを作成することにします。
図 25: Products
テーブルに新しい行を追加するメソッドを作成する (フルサイズの画像を表示する をクリックします)
次の画面に InsertCommand
の CommandText
が表示されます。 このクエリを拡張するには、同じスコープ内の SELECT SCOPE_IDENTITY()
列に挿入された最後の ID 値を返す IDENTITY
をクエリの末尾に追加します。 (の詳細と、@@IDENTITYのSCOPE_IDENTITY()
する理由については、技術ドキュメントを参照してください)。
INSERT
ステートメントを追加する前に、SELECT
ステートメントがセミコロンで終了していることを確認してください。
図 26: SCOPE_IDENTITY()
値を返すようにクエリを拡張する (フルサイズの画像を表示する をクリックします)
最後に、新しいメソッド InsertProduct
に名前を付けます。
図 27: 新しいメソッド名を InsertProduct
に設定します (フルサイズの画像を表示する をクリックします)。
DataSet デザイナーに戻ると、ProductsTableAdapter
に新しいメソッド、InsertProduct
が含まれていることがわかります。 この新しいメソッドの Products
テーブル内の各列にパラメーターが存在しない場合は、INSERT
ステートメントがセミコロンで終了していない可能性があります。
InsertProduct
メソッドを構成し、INSERT
、SELECT
ステートメントをセミコロンで区切っていることを確認します。
既定では、挿入メソッドはクエリ以外のメソッドを発行し、影響を受ける行数が返されます。 ただし、InsertProduct
メソッドで影響を受ける行数ではなく、クエリによって返された値を返す必要があります。 これを実現するには、InsertProduct
メソッドの ExecuteMode
プロパティを Scalar
に調整します。
図 28: ExecuteMode
プロパティを Scalar
に変更します (フルサイズの画像を表示する をクリックします)。
次のコードは、この新しい InsertProduct
メソッドの動作を示しています。
Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
Dim new_productID As Integer = Convert.ToInt32(productsAdapter.InsertProduct( _
"New Product", 1, 1, "12 tins per carton", 14.95, 10, 0, 10, false))
productsAdapter.Delete(new_productID)
手順 5: データ アクセス層の完了
ProductsTableAdapters
クラスは CategoryID
テーブルから SupplierID
と Products
値を返しますが、CategoryName
テーブルの Categories
列や CompanyName
テーブルの Suppliers
列は含まれません。とはいえ、これらは製品情報を表示する際に表示したい列である可能性が高いことに注意してください。 TableAdapter の初期メソッド GetProducts()
を拡張して、CategoryName
および CompanyName
列の両方の値を含めることができます。これにより、厳密に型指定された DataTable が更新され、これらの新しい列も含まれるようになります。
ただし、TableAdapter のデータの挿入、更新、削除メソッドはこの初期メソッドに基づいているため、問題が発生する可能性があります。 幸いにも、挿入、更新、削除のための自動生成メソッドは、SELECT
句のサブクエリの影響を受けません。
Categories
と Suppliers
にクエリをサブクエリとして追加し、JOIN
として追加しないよう慎重に操作することで、データを変更するためにメソッドを再作業する必要がなくなります。
GetProducts()
で ProductsTableAdapter
メソッドを右クリックし、[構成] を選択します。 次に、SELECT
句を次のように調整します。
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
(SELECT CategoryName FROM Categories
WHERE Categories.CategoryID = Products.CategoryID) as CategoryName,
(SELECT CompanyName FROM Suppliers
WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName
FROM Products
図 29: SELECT
メソッドのGetProducts()
ステートメントを更新します (フルサイズの画像を表示する をクリックします)。
この新しいクエリを使用するように GetProducts()
メソッドを更新すると、DataTable には 2 つの新しい列 CategoryName
、SupplierName
が含まれます。
図 30: Products
DataTable に 2 つの新しい列がある
少し時間をあけて、SELECT
メソッド内の GetProductsByCategoryID(categoryID)
句も更新します。
GetProducts()
構文を使用してSELECT
JOIN
を更新すると、DataSet Designer は、DB ダイレクト パターンを使用してデータベース データを挿入、更新、および削除するためのメソッドを自動生成できません。 代わりに、このチュートリアルの前半で InsertProduct
メソッドで行ったのと同様に、それらを手動で作成する必要があります。 さらに、バッチ更新パターンを使用する場合は、手動で、InsertCommand
、UpdateCommand
、DeleteCommand
プロパティの値を指定する必要があります。
残りの TableAdapter の追加
これまでは、1 つのデータベース テーブルの単一の TableAdapter を操作する方法についてのみ説明してきました。 ただし、Northwind データベースには、Web アプリケーションで操作する必要がある関連テーブルがいくつか含まれています。 型指定されたデータセットには、複数の関連する DataTable を含めることができます。 このため、DAL を完了するには、これらのチュートリアルで使用する他のテーブルの DataTable を追加する必要があります。 型指定されたデータセットに新しい TableAdapter を追加するには、データセット デザイナーを開き、デザイナー内を右クリックし、[追加]/[TableAdapter] を選択します。 これにより、新しい DataTable と TableAdapter が作成され、このチュートリアルの前半で確認したウィザードに移動します。
次のクエリを使用して、次の TableAdapters とメソッドを作成するには数分かかります。
ProductsTableAdapter
内のクエリには、各製品のカテゴリ名とサプライヤー名を取得するためのサブクエリが含まれていることに注意してください。 さらに、フォローしている場合は、ProductsTableAdapter
クラスの GetProducts()
、GetProductsByCategoryID(categoryID)
メソッドが既に追加されています。
製品TableAdapter
GetProducts:
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products
GetProductsByCategoryIDです。
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products WHERE CategoryID = @CategoryID
GetProductsBySupplierIDです。
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products WHERE SupplierID = @SupplierID
GetProductByProductIDです。
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued, (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID) as CategoryName, (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName FROM Products WHERE ProductID = @ProductID
カテゴリーTableAdapter
GetCategories:
SELECT CategoryID, CategoryName, Description FROM Categories
GetCategoryByCategoryIDです。
SELECT CategoryID, CategoryName, Description FROM Categories WHERE CategoryID = @CategoryID
サプライヤーテーブルアダプター
GetSuppliers:
SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM Suppliers
GetSuppliersByCountry:
SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM Suppliers WHERE Country = @Country
GetSupplierBySupplierID です。
SELECT SupplierID, CompanyName, Address, City, Country, Phone FROM Suppliers WHERE SupplierID = @SupplierID
従業員テーブル アダプター
GetEmployees:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees
マネージャーによる社員取得:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees WHERE ReportsTo = @ManagerID
GetEmployeeByEmployeeIDです。
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees WHERE EmployeeID = @EmployeeID
図 31: 4 つの TableAdapters が追加された後の DataSet デザイナー (フルサイズの画像を表示する をクリックします)
DAL へのカスタム コードの追加
型指定された DataSet に追加された TableAdapters と DataTables は、XML スキーマ定義ファイル (Northwind.xsd
) として表されます。 このスキーマ情報を表示するには、ソリューション エクスプローラーで Northwind.xsd
ファイルを右クリックし、[コードの表示] を選択します。
図 32: Northwinds 型指定された DataSet の XML スキーマ定義 (XSD) ファイル (フルサイズの画像を表示する をクリックします)
このスキーマ情報は、コンパイル時または実行時に (必要に応じて) C# または Visual Basic コードに変換され、この時点でデバッガーを使用してステップ実行することができます。 この自動生成されたコードを表示するには、クラス ビューに移動し、TableAdapter または型指定されたデータセット クラスの詳細を表示します。 画面にクラス ビューが表示されない場合は、[表示] メニューに移動して選択するか、Ctrl + Shift + C キーを押します。 クラス ビューから、型指定されたデータセットおよび TableAdapter クラスのプロパティ、メソッド、イベントを確認できます。 特定のメソッドのコードを表示するには、クラス ビューでメソッド名をダブルクリックするか、右クリックして [定義へ移動] を選択します。
図 33: クラス ビューから [定義に移動] を選択して自動生成されたコードを検査する
自動生成されたコードにより時間を大幅に節約できますが、コードは非常に汎用的があることが多く、アプリケーション固有のニーズを合わせてカスタマイズする必要があります。 自動生成されたコードを拡張するリスクは、コードを生成したツールがカスタマイズを "再生成" して上書きする必要があると判断する可能性があるということです。 .NET 2.0 の新しい部分クラスの概念により、クラスを複数のファイルに簡単に分割できます。 これにより、Visual Studio がカスタマイズを上書きすることを心配せずに、独自のメソッド、プロパティ、イベントを自動生成されたクラスに追加できます。
DAL をカスタマイズする方法を示すために、GetProducts()
クラスに SuppliersRow
メソッドを追加してみましょう。
SuppliersRow
クラスは Suppliers
テーブル内の単一のレコードを表します。各サプライヤーは 0 個以上の製品を提供できるため、GetProducts()
は指定されたサプライヤーの製品を返します。 これを実現するには、App_Code
フォルダーで SuppliersRow.vb
という名前の新しいクラス ファイルを作成し、次のコードを追加します。
Imports NorthwindTableAdapters
Partial Public Class Northwind
Partial Public Class SuppliersRow
Public Function GetProducts() As Northwind.ProductsDataTable
Dim productsAdapter As New ProductsTableAdapter
Return productsAdapter.GetProductsBySupplierID(Me.SupplierID)
End Function
End Class
End Class
この部分クラスは、Northwind.SuppliersRow
クラスをビルドする際に、定義した GetProducts()
メソッドを含むようにコンパイラに指示します。 プロジェクトをビルドしてクラス ビューに戻ると、GetProducts()
が Northwind.SuppliersRow
のメソッドとして一覧表示されます。
図 34: GetProducts()
メソッドが Northwind.SuppliersRow
クラスの一部になりました
次のコードが示すように、GetProducts()
メソッドを使用して、特定のサプライヤーの製品セットを列挙できるようになりました。
Dim suppliersAdapter As New NorthwindTableAdapters.SuppliersTableAdapter()
Dim suppliers As Northwind.SuppliersDataTable = suppliersAdapter.GetSuppliers()
For Each supplier As Northwind.SuppliersRow In suppliers
Response.Write("Supplier: " & supplier.CompanyName)
Response.Write("<ul>")
Dim products As Northwind.ProductsDataTable = supplier.GetProducts()
For Each product As Northwind.ProductsRow In products
Response.Write("<li>" & product.ProductName & "</li>")
Next
Response.Write("</ul><p> </p>")
Next
このデータは、任意の ASP.NET のデータ Web コントロールで表示することもできます。 次のページでは、2 つのフィールドを持つ GridView コントロールを使用します。
- 各サプライヤーの名前を表示する BoundField と
- 各サプライヤーの
GetProducts()
メソッドによって返される結果にバインドされる BulletedList コントロールを含む TemplateField。
このようなマスター/詳細レポートを表示する方法については、今後のチュートリアルで説明します。 この例は、Northwind.SuppliersRow
クラスに追加されたカスタム メソッドの使用を説明することを目的としています。
SuppliersAndProducts.aspx
<%@ Page Language="VB" CodeFile="SuppliersAndProducts.aspx.vb"
AutoEventWireup="true" Inherits="SuppliersAndProducts" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>
Suppliers and Their Products</h1>
<p>
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False"
CssClass="DataWebControlStyle">
<HeaderStyle CssClass="HeaderStyle" />
<AlternatingRowStyle CssClass="AlternatingRowStyle" />
<Columns>
<asp:BoundField DataField="CompanyName"
HeaderText="Supplier" />
<asp:TemplateField HeaderText="Products">
<ItemTemplate>
<asp:BulletedList ID="BulletedList1"
runat="server" DataSource="<%# CType(CType(Container.DataItem, System.Data.DataRowView).Row, Northwind.SuppliersRow).GetProducts() %>"
DataTextField="ProductName">
</asp:BulletedList>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</p>
</div>
</form>
</body>
</html>
SuppliersAndProducts.aspx.vb
Imports NorthwindTableAdapters
Partial Class SuppliersAndProducts
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load
Dim suppliersAdapter As New SuppliersTableAdapter
GridView1.DataSource = suppliersAdapter.GetSuppliers()
GridView1.DataBind()
End Sub
End Class
図 35: 仕入先の会社名が左側の列に表示され、右側の製品 (フルサイズの画像を表示するにはクリックします)
まとめ
Web アプリケーションを構築する際の最初の手順の 1 つに、プレゼンテーション層の作成を開始する前に行う DAL の作成があります。 Visual Studio を使用すると、型指定されたデータセットに基づく DAL を作成するタスクを、コードを 1 行も記述することなく 10 - 15 分で実行できます。 以降のチュートリアルは、この DAL に基づいています。 次の チュートリアル では、いくつかのビジネス ルールを定義し、それらを別のビジネス ロジック レイヤーに実装する方法を確認します。
プログラミングに満足!
もっと読む
この記事で説明したトピックの詳細については、次のリソースを参照してください。
- VS 2005 および ASP.NET 2.0 で厳密に型指定された TableAdapters と DataTable を使用して DAL を構築する
- データ層コンポーネントの設計と層を介したデータの受け渡し
- ASP.NET 2.0 アプリケーションでの構成情報の暗号化
- TableAdapter の概要
- 型付きデータセットの操作
- Visual Studio 2005 および ASP.NET 2.0 での Strongly-Typed データ アクセスの使用
- TableAdapter メソッドを拡張する方法
このチュートリアルに含まれるトピックに関するビデオ トレーニング
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジを使用しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・セルフ ASP.NET 24時間で2.0です。 彼には mitchell@4GuysFromRolla.comで連絡できます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Ron Green、Hilton Giesenow、Dennis Patterson、Liz Shulok、Abel Gomez、Carlos Santos でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.comにメッセージを送ってください。