データ アクセス層を作成する (VB)
このチュートリアルでは、まず型指定されたデータセットを使用してデータ アクセス層 (DAL) を作成し、データベース内の情報にアクセスします。
はじめに
Web 開発者の作業の中心は、データを操作することです。 データを保存するためのデータベース、データを取得して変更するためのコード、データを収集して集計するための Web ページを作成します。 これは、ASP.NET 2.0 でこれらの一般的なパターンを実装するための手法について説明する、長いシリーズの最初のチュートリアルになります。 まず、型指定されたデータセットを使用したデータ アクセス層 (DAL)、カスタム ビジネス ルールを適用するビジネス ロジック層 (BLL)、共通ページ レイアウトを共有する ASP.NET ページからなるプレゼンテーション層で構成されたソフトウェア アーキテクチャを作成します。 このバックエンドの下準備が完了したら、レポートに進み、Web アプリケーションからデータを表示、集計、収集、検証する方法を示します。 これらのチュートリアルは簡潔で、プロセスを視覚的に説明するためのスクリーン ショットを豊富に含む詳細な手順を説明することを目的としています。 チュートリアルにはそれぞれ C# と Visual Basic のバージョンが用意されており、使用される完全なコードのダウンロードが含まれています (この最初のチュートリアルは非常に長いですが、残りのチュートリアルはより消化しやすい短いセクションで提供されています)。
これらのチュートリアルでは、App_Data
ディレクトリに配置された Northwind データベースの Microsoft SQL Server 2005 Express Edition バージョンを使用します。 App_Data
フォルダーには、データベース ファイルのほか、別のデータベース バージョンを使用する場合に備えて、データベースを作成するための SQL スクリプトも含まれています。 Northwind データベースの別の SQL Server バージョンを使用する場合は、Web.config
ファイルの NORTHWNDConnectionString
設定を更新する必要があります。 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 サイトを作成します (クリックするとフルサイズの画像が表示されます)
これにより、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)
は、特定の製品に関する情報を返します
これらのメソッドを呼び出すと、データベースに接続されて適切なクエリが発行され、結果が返されます。 結果が返される方法は重要です。 これらのメソッドは、データベース クエリによって設定されたデータセットまたは DataReader を単純に返すことができますが、厳密に型指定されたオブジェクトを使用してこれらの結果を返すのが理想的です。 厳密に型指定されたオブジェクトは、コンパイル時にスキーマが厳密に定義される一方、弱く型指定されたオブジェクトは実行時までスキーマが不明です。
たとえば、DataReader とデータセット (既定) は、データの設定に使用されるデータベース クエリが返す列によってスキーマが定義されるため、弱く型指定されたオブジェクトです。 弱く型指定された DataTable から特定の列にアクセスするには、次のような構文を使用する必要があります。DataTable.Rows(index)("columnName")
この例では、文字列または序数インデックスを使用して列名にアクセスする必要があるため、DataTable が弱く型指定されていることを示しています。 一方、厳密に型指定された DataTable では、各列がプロパティとして実装されており、次のようなコードになります。DataTable.Rows(index).columnName
厳密に型指定されたオブジェクトを返すには、開発者が独自のカスタム ビジネス オブジェクトを作成するか、型指定されたデータセットを使用します。 ビジネス オブジェクトは、そのプロパティが通常、ビジネス オブジェクトが表す基になるデータベース テーブルの列を反映するクラスとして、開発者によって実装されます。 型指定されたデータセットは、データベース スキーマに基づいて Visual Studio によって生成され、そのメンバーがこのスキーマに従って厳密に型指定されたクラスです。 型指定されたデータセット自体は、ADO.NET データセット、DataTable、および DataRow クラスを拡張するクラスで構成されます。 型指定されたデータセットには、厳密に型指定された DataTable に加えて、データセットの DataTable にデータを設定し、DataTable 内の変更をデータベースに反映するためのメソッドを持つクラスである TableAdapter も含まれるようになりました。
Note
型指定されたデータセットとカスタム ビジネス オブジェクトを使用する場合の長所と短所の詳細については、「データ層コンポーネントの設計と層を介したデータの受け渡し」を参照してください。
これらのチュートリアルのアーキテクチャでは、厳密に型指定されたデータセットを使用します。 図 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: ドロップダウン リストから 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: [INSERT、UPDATE、および DELETE ステートメントの生成] オプションのみを選択します (クリックするとフルサイズの画像が表示されます)
詳細オプションを確認したら、[次へ] をクリックして最後の画面に進みます。 ここで、TableAdapter に追加するメソッドを選択するように求められます。 データを設定するには、次の 2 つのパターンがあります。
- DataTable にデータを格納する この方法では、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: 型指定された DataSet に Products
DataTable と ProductsTableAdapter
が追加されます (クリックするとフルサイズの画像が表示されます)
この時点で、単一の 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 コントロールにバインドできます。 次の例は、Page_Load
イベント ハンドラー内でわずか 3 行のコードを使用して、GetProducts()
メソッドによって返される 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 を右クリックし、[クエリの追加] を選択します
最初に、アドホック SQL ステートメントを使用してデータベースにアクセスするか、新規または既存のストアド プロシージャを使用するかを確認するメッセージが表示されます。 もう一度、アドホック SQL ステートメントを使用してみましょう。 次に、使用する SQL クエリの種類を尋ねられます。 指定したカテゴリに属するすべての製品を返す必要があるため、行を返す SELECT
ステートメントを記述します。
図 15: 行を返す SELECT
ステートメントの作成を選択します (クリックするとフルサイズの画像が表示されます)
次の手順では、データへのアクセスに使用する SQL クエリを定義します。 特定のカテゴリに属する製品のみを返す必要があるため、GetProducts()
から同じ SELECT
ステートメントを使用しますが、次の WHERE
句を追加します: WHERE CategoryID = @CategoryID
。 @CategoryID
パラメーターは、作成中のメソッドが対応する型の入力パラメーター (つまり Null 許容整数) を必要とすることを TableAdapter ウィザードに示します。
図 16: 指定したカテゴリの製品のみを返すクエリを入力します (クリックするとフルサイズの画像が表示されます)
最後の手順では、使用するデータ アクセス パターンを選択し、生成されるメソッドの名前をカスタマイズできます。 Fill パターンの場合、戻り値の DataTable 戻りパターン (GetX
メソッド) の名前を FillByCategoryID
に変更し、GetProductsByCategoryID
を使用してみましょう。
図 17: TableAdapter のメソッドの名前を選択します (クリックするとフルサイズの画像を表示されます)
ウィザードが完了すると、データセット デザイナーに新しい TableAdapter メソッドが含まれます。
図 18: 製品のクエリをカテゴリ別に実行できるようになりました
少し時間を取って、同じ手法を使用して GetProductByProductID(productID)
メソッドを追加します。
これらのパラメーター化されたクエリは、データセット デザイナーから直接テストできます。 TableAdapter でメソッドを右クリックし、[データのプレビュー] を選択します。 次に、パラメーターに使用する値を入力し、[プレビュー] をクリックします。
図 19: Beverages カテゴリに属する製品が表示されます (クリックするとフルサイズの画像が表示されます)
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: Beverages カテゴリの製品が表示されます (クリックするとフルサイズの画像が表示されます)
手順 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: 更新メソッドを呼び出すとすべての変更がデータベースと同期されます (クリックするとフルサイズの画像が表示されます)
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 つの列のみを更新する場合や、新しく挿入されたレコードの IDENTITY
(自動増分) フィールドの値を返す、カスタマイズされた Insert()
メソッドが必要な場合があります。
このようなカスタム メソッドを作成するには、データセット デザイナーに戻ります。 TableAdapter を右クリックし、[クエリの追加] を選択し、TableAdapter ウィザードに戻ります。 2 番目の画面では、作成するクエリの種類を示すことができます。 新しい製品を追加し、新しく追加されたレコードの ProductID
の値を返すメソッドを作成してみましょう。 このため、INSERT
クエリを作成することにします。
図 25: Products
テーブルに新しい行を追加するメソッドを作成します (クリックするとフルサイズの画像が表示されます)
次の画面に InsertCommand
の CommandText
が表示されます。 このクエリを拡張するには、同じスコープ内の IDENTITY
列に挿入された最後の ID 値を返す SELECT SCOPE_IDENTITY()
をクエリの末尾に追加します。 (SCOPE_IDENTITY()
の詳細と、@@IDENTITY の代わりに SCOPE_IDENTITY() を使用する理由については、技術文書を参照してください。) SELECT
ステートメントを追加する前に、INSERT
ステートメントがセミコロンで終了していることを確認してください。
図 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
クラスは Products
テーブルから CategoryID
と SupplierID
値を返しますが、Categories
テーブルの CategoryName
列や Suppliers
テーブルの CompanyName
列は含まれません。とはいえ、これらは製品情報を表示する際に表示したい列である可能性が高いことに注意してください。 TableAdapter の初期メソッド GetProducts()
を拡張して、CategoryName
および CompanyName
列の両方の値を含めることができます。これにより、厳密に型指定された DataTable が更新され、これらの新しい列も含まれるようになります。
ただし、TableAdapter のデータの挿入、更新、削除メソッドはこの初期メソッドに基づいているため、問題が発生する可能性があります。 幸いにも、挿入、更新、削除のための自動生成メソッドは、SELECT
句のサブクエリの影響を受けません。 Categories
と Suppliers
にクエリをサブクエリとして追加し、JOIN
として追加しないよう慎重に操作することで、データを変更するためにメソッドを再作業する必要がなくなります。 ProductsTableAdapter
で GetProducts()
メソッドを右クリックし、[構成] を選択します。 次に、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: GetProducts()
メソッドの SELECT
ステートメントを更新します (クリックするとフルサイズの画像が表示されます)
この新しいクエリを使用するように GetProducts()
メソッドを更新すると、DataTable には 2 つの新しい列 CategoryName
、SupplierName
が含まれます。
図 30: Products
DataTable に 2 つの新しい列が含まれます
少し時間をあけて、GetProductsByCategoryID(categoryID)
メソッド内の SELECT
句も更新します。
JOIN
構文を使用してGetProducts()
SELECT
を更新すると、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)
メソッドが既に追加されています。
ProductsTableAdapter
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
CategoriesTableAdapter
GetCategories:
SELECT CategoryID, CategoryName, Description FROM Categories
GetCategoryByCategoryID:
SELECT CategoryID, CategoryName, Description FROM Categories WHERE CategoryID = @CategoryID
SuppliersTableAdapter
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
EmployeesTableAdapter
GetEmployees:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country FROM Employees
GetEmployeesByManager:
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 つの TableAdapter が追加された後のデータセット デザイナー (クリックするとフルサイズの画像を表示されます)
DAL へのカスタム コードの追加
型指定された DataSet に追加された TableAdapters と DataTables は、XML スキーマ定義ファイル (Northwind.xsd
) として表されます。 このスキーマ情報を表示するには、ソリューション エクスプローラーで Northwind.xsd
ファイルを右クリックし、[コードの表示] を選択します。
図 32: Northwind の型指定されたデータセットの XML スキーマ定義 (XSD) ファイル (クリックするとフルサイズの画像を表示されます)
このスキーマ情報は、コンパイル時または実行時に (必要に応じて) C# または Visual Basic コードに変換され、この時点でデバッガーを使用してステップ実行することができます。 この自動生成されたコードを表示するには、クラス ビューに移動し、TableAdapter または型指定されたデータセット クラスの詳細を表示します。 画面にクラス ビューが表示されない場合は、[表示] メニューに移動して選択するか、Ctrl + Shift + C キーを押します。 クラス ビューから、型指定されたデータセットおよび TableAdapter クラスのプロパティ、メソッド、イベントを確認できます。 特定のメソッドのコードを表示するには、クラス ビューでメソッド名をダブルクリックするか、右クリックして [定義へ移動] を選択します。
図 33: クラス ビューから [定義へ移動] を選択し、自動生成されたコードを調べます
自動生成されたコードにより時間を大幅に節約できますが、コードは非常に汎用的があることが多く、アプリケーション固有のニーズを合わせてカスタマイズする必要があります。 自動生成されたコードを拡張するリスクは、コードを生成したツールがカスタマイズを "再生成" して上書きする必要があると判断する可能性があるということです。 .NET 2.0 の新しい部分クラスの概念により、クラスを複数のファイルに簡単に分割できます。 これにより、Visual Studio がカスタマイズを上書きすることを心配せずに、独自のメソッド、プロパティ、イベントを自動生成されたクラスに追加できます。
DAL をカスタマイズする方法を示すために、SuppliersRow
クラスに GetProducts()
メソッドを追加してみましょう。 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 での厳密に型指定された TableAdapter と DataTable を使用した DAL の作成
- データ層コンポーネントの設計と層を介したデータの受け渡し
- ASP.NET 2.0 アプリケーションでの構成情報の暗号化
- TableAdapter の概要
- 型指定されたデータセットの操作
- Visual Studio 2005 と ASP.NET 2.0 での厳密に型指定されたデータ アクセスの使用
- TableAdapter メソッドを拡張する方法
このチュートリアルに含まれるトピックに関するビデオ トレーニング
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Ron Green、Hilton Giesenow、Dennis Patterson、Liz Shulok、Abel Gomez、Carlos Santos でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。