前のチュートリアルでは、コードに SQL ステートメントを作成し、実行するデータベースにステートメントを渡しました。 別の方法として、SQL ステートメントがデータベースで事前に定義されているストアド プロシージャを使用します。 このチュートリアルでは、TableAdapter ウィザードで新しいストアド プロシージャを生成する方法について説明します。
イントロダクション
これらのチュートリアルのデータ アクセス層 (DAL) では、型指定されたデータセットを使用します。 データ アクセス層の作成チュートリアルで説明したように、型指定された DataSet は厳密に型指定された DataTable と TableAdapters で構成されます。 DataTable はシステム内の論理エンティティを表し、TableAdapters は基になるデータベースとインターフェイスしてデータ アクセス作業を実行します。 これには、DataTable にデータの設定、スカラー データを返すクエリの実行、データベースからのレコードの挿入、更新、および削除が含まれます。
TableAdapters によって実行される SQL コマンドには、 SELECT columnList FROM TableName
などのアドホック SQL ステートメントまたはストアド プロシージャのいずれかを指定できます。 このアーキテクチャの TableAdapters では、アドホック SQL ステートメントが使用されます。 ただし、多くの開発者とデータベース管理者は、セキュリティ、保守容易性、および更新容易性の理由から、アドホック SQL ステートメントよりもストアド プロシージャを優先します。 他のユーザーは、柔軟性のためにアドホック SQL ステートメントを熱心に好みます。 私の仕事では、アドホック SQL ステートメントよりもストアド プロシージャを優先していますが、アドホック SQL ステートメントを使用して、前のチュートリアルを簡略化することを選択しました。
TableAdapter を定義したり、新しいメソッドを追加したりする場合、TableAdapter ウィザードを使用すると、アドホック SQL ステートメントを使用する場合と同じように、新しいストアド プロシージャの作成や既存のストアド プロシージャの使用が簡単になります。 このチュートリアルでは、TableAdapter ウィザードでストアド プロシージャを自動生成する方法について説明します。 次のチュートリアルでは、既存のストアド プロシージャまたは手動で作成されたストアド プロシージャを使用するように TableAdapter のメソッドを構成する方法について説明します。
注
ストアド プロシージャとアドホック SQL の長所と短所に関する活発な議論については、Rob Howard のブログ エントリ「 ストアド プロシージャをまだ使用しない」 をご覧ください。 また、Frans Bouma のブログ エントリのストアド プロシージャは Bad、M Kay? をご覧ください。
ストアド プロシージャの基本
関数は、すべてのプログラミング言語に共通するコンストラクトです。 関数は、関数が呼び出されたときに実行されるステートメントのコレクションです。 関数は入力パラメーターを受け取ることができ、必要に応じて値を返すことができます。
ストアド プロシージャ は、プログラミング言語の関数と多くの類似点を共有するデータベースコンストラクトです。 ストアド プロシージャは、ストアド プロシージャが呼び出されたときに実行される一連の T-SQL ステートメントで構成されます。 ストアド プロシージャは、多くの入力パラメーターに対して 0 を受け取り、スカラー値、出力パラメーター、または最も一般的には、 SELECT
クエリから結果セットを返すことができます。
注
ストアド プロシージャは、多くの場合、sprocs または SP と呼ばれます。
ストアド プロシージャは、 CREATE PROCEDURE
T-SQL ステートメントを使用して作成されます。 たとえば、次の T-SQL スクリプトは、GetProductsByCategoryID
という名前の単一のパラメーターを受け取り、一致する@CategoryID
値を持つProductID
テーブル内の列のProductName
、UnitPrice
、Discontinued
、およびProducts
フィールドを返す、CategoryID
という名前のストアド プロシージャを作成します。
CREATE PROCEDURE GetProductsByCategoryID
(
@CategoryID int
)
AS
SELECT ProductID, ProductName, UnitPrice, Discontinued
FROM Products
WHERE CategoryID = @CategoryID
このストアド プロシージャが作成されたら、次の構文を使用して呼び出すことができます。
EXEC GetProductsByCategory categoryID
注
次のチュートリアルでは、Visual Studio IDE を使用してストアド プロシージャを作成する方法について説明します。 ただし、このチュートリアルでは、TableAdapter ウィザードでストアド プロシージャを自動的に生成できるようにします。
ストアド プロシージャは、単にデータを返すだけでなく、多くの場合、1 つのトランザクションのスコープ内で複数のデータベース コマンドを実行するために使用されます。 たとえば、 DeleteCategory
という名前のストアド プロシージャは、 @CategoryID
パラメーターを取り込み、2 つの DELETE
ステートメントを実行する場合があります。1 つ目は関連製品を削除するため、もう 1 つは指定したカテゴリを削除することです。 ストアド プロシージャ内の複数のステートメントは、トランザクション内で自動的にラップ されません 。 ストアド プロシージャの複数のコマンドがアトミック操作として扱われるように、追加の T-SQL コマンドを発行する必要があります。 後続のチュートリアルでは、トランザクションのスコープ内でストアド プロシージャのコマンドをラップする方法について説明します。
アーキテクチャ内でストアド プロシージャを使用する場合、データ アクセス層のメソッドは、アドホック SQL ステートメントを発行するのではなく、特定のストアド プロシージャを呼び出します。 これにより、アプリケーションのアーキテクチャ内で定義されるのではなく、(データベース上で) 実行される SQL ステートメントの場所が一元化されます。 この一元化により、クエリの検索、分析、チューニングが容易になり、データベースが使用されている場所と方法について、より明確な画像が得られます。
ストアド プロシージャの基礎の詳細については、このチュートリアルの最後にある「さらに読む」セクションのリソースを参照してください。
手順 1: 高度なデータ アクセス層のシナリオ Web ページを作成する
ストアド プロシージャを使用して DAL を作成する方法について説明する前に、まず、このチュートリアルと次のいくつかのチュートリアルに必要な ASP.NET ページを Web サイト プロジェクトに作成してみましょう。 まず、 AdvancedDAL
という名前の新しいフォルダーを追加します。 次に、次の ASP.NET ページをそのフォルダーに追加し、各ページを Site.master
マスター ページに関連付けます。
Default.aspx
NewSprocs.aspx
ExistingSprocs.aspx
JOINs.aspx
AddingColumns.aspx
ComputedColumns.aspx
EncryptingConfigSections.aspx
ManagedFunctionsAndSprocs.aspx
図 1: 高度なデータ アクセス層のシナリオに関するチュートリアルの ASP.NET ページを追加する
他のフォルダーと同様に、Default.aspx
フォルダーにAdvancedDAL
すると、そのセクションにチュートリアルが一覧表示されます。
SectionLevelTutorialListing.ascx
ユーザー コントロールがこの機能を提供していることを思い出してください。 そのため、ソリューション エクスプローラーからページのデザイン ビューにドラッグして、このユーザー コントロールを Default.aspx
に追加します。
図 2: SectionLevelTutorialListing.ascx
に Default.aspx
ユーザー コントロールを追加します (フルサイズの画像を表示する をクリックします)。
最後に、これらのページをエントリとして Web.sitemap
ファイルに追加します。 具体的には、[バッチ データの操作] <siteMapNode>
の後に次のマークアップを追加します。
<siteMapNode url="~/AdvancedDAL/Default.aspx"
title="Advanced DAL Scenarios"
description="Explore a number of advanced Data Access Layer scenarios.">
<siteMapNode url="~/AdvancedDAL/NewSprocs.aspx"
title="Creating New Stored Procedures for TableAdapters"
description="Learn how to have the TableAdapter wizard automatically
create and use stored procedures." />
<siteMapNode url="~/AdvancedDAL/ExistingSprocs.aspx"
title="Using Existing Stored Procedures for TableAdapters"
description="See how to plug existing stored procedures into a
TableAdapter." />
<siteMapNode url="~/AdvancedDAL/JOINs.aspx"
title="Returning Data Using JOINs"
description="Learn how to augment your DataTables to work with data
returned from multiple tables via a JOIN query." />
<siteMapNode url="~/AdvancedDAL/AddingColumns.aspx"
title="Adding DataColumns to a DataTable"
description="Master adding new columns to an existing DataTable." />
<siteMapNode url="~/AdvancedDAL/ComputedColumns.aspx"
title="Working with Computed Columns"
description="Explore how to work with computed columns when using
Typed DataSets." />
<siteMapNode url="~/AdvancedDAL/EncryptingConfigSections.aspx"
title="Protected Connection Strings in Web.config"
description="Protect your connection string information in
Web.config using encryption." />
<siteMapNode url="~/AdvancedDAL/ManagedFunctionsAndSprocs.aspx"
title="Creating Managed SQL Functions and Stored Procedures"
description="See how to create SQL functions and stored procedures
using managed code." />
</siteMapNode>
Web.sitemap
を更新した後、ブラウザーを使用してチュートリアル Web サイトを表示します。 左側のメニューには、高度な DAL シナリオのチュートリアルの項目が含まれるようになりました。
図 3: サイト マップに、高度な DAL シナリオチュートリアルのエントリが含まれるようになりました
手順 2: TableAdapter を構成して新しいストアド プロシージャを作成する
アドホック SQL ステートメントではなくストアド プロシージャを使用するデータ アクセス層を作成する方法を示すために、~/App_Code/DAL
という名前のNorthwindWithSprocs.xsd
フォルダーに新しい型指定された DataSet を作成します。 前のチュートリアルでこのプロセスを詳しく説明したので、ここでの手順をすばやく進めます。 型指定された DataSet の作成と構成に関する詳細な手順が必要な場合は、 データ アクセス層の作成 に関するチュートリアルを参照してください。
図 4 に示すように、 DAL
フォルダーを右クリックし、[新しい項目の追加] を選択し、DataSet テンプレートを選択して、新しい DataSet をプロジェクトに追加します。
図 4: 新しい型指定されたデータセットをプロジェクトの名前付き NorthwindWithSprocs.xsd
に追加します (フルサイズの画像を表示する をクリックします)。
これにより、新しい型指定された DataSet が作成され、デザイナーが開き、新しい TableAdapter が作成され、TableAdapter 構成ウィザードが起動します。 TableAdapter 構成ウィザードの最初の手順では、使用するデータベースを選択するように求められます。 Northwind データベースへの接続文字列がドロップダウン リストに表示されます。 これを選択し、[次へ] をクリックします。
この次の画面から、TableAdapter がデータベースにアクセスする方法を選択できます。 前のチュートリアルでは、SQL ステートメントを使用する最初のオプションを選択しました。 このチュートリアルでは、2 番目のオプションである [新しいストアド プロシージャの作成] を選択し、[次へ] をクリックします。
図 5: 新しいストアド プロシージャを作成するように TableAdapter に指示する (フルサイズの画像を表示する をクリックします)
アドホック SQL ステートメントの使用と同様に、次の手順では、TableAdapter のメイン クエリに SELECT
ステートメントを指定するように求められます。 ただし、ここで入力した SELECT
ステートメントを使用してアドホック クエリを直接実行する代わりに、TableAdapter ウィザードによって、この SELECT
クエリを含むストアド プロシージャが作成されます。
この TableAdapter には、次の SELECT
クエリを使用します。
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued
FROM Products
図 6: SELECT
クエリを入力します (フルサイズの画像を表示する をクリックします)
注
上記のクエリは、ProductsTableAdapter
型指定 DataSet のNorthwind
のメイン クエリとは若干異なります。
ProductsTableAdapter
Typed DataSet のNorthwind
には、各製品のカテゴリと仕入先のカテゴリ名と会社名を取り戻すための 2 つの相関サブクエリが含まれていることを思い出してください。 今後の JOIN を使用するための TableAdapter の更新 に関するチュートリアルでは、この関連データをこの TableAdapter に追加する方法について説明します。
[詳細オプション] ボタンをクリックします。 ここから、ウィザードで TableAdapter の挿入、更新、および削除ステートメントも生成するかどうか、オプティミスティック コンカレンシーを使用するかどうか、および挿入と更新後にデータ テーブルを更新するかどうかを指定できます。 [挿入、更新、および削除ステートメントの生成] オプションは、既定でオンになっています。 オンのままにします。 このチュートリアルでは、[オプティミスティック コンカレンシーを使用する] オプションはオフのままにします。
TableAdapter ウィザードによってストアド プロシージャを自動的に作成すると、[データ テーブルの更新] オプションは無視されます。 このチェック ボックスがオンになっているかどうかに関係なく、結果の挿入および更新ストアド プロシージャは、手順 3 で示すように、just-inserted または just-updated レコードを取得します。
図 7: [挿入、更新、および削除ステートメントの生成] オプションをオンのままにする
注
[オプティミスティック コンカレンシーの使用] オプションがオンの場合、ウィザードは、他のフィールドに変更があった場合にデータが更新されないようにする条件を WHERE
句に追加します。 TableAdapter の組み込みの オプティミスティック コンカレンシー 制御機能の使用方法の詳細については、オプティミスティック コンカレンシーの実装に関するチュートリアルを参照してください。
SELECT
クエリを入力し、[挿入、更新、および削除ステートメントの生成] オプションがオンになっていることを確認したら、[次へ] をクリックします。 図 8 に示す次の画面では、データの選択、挿入、更新、および削除のためにウィザードによって作成されるストアド プロシージャの名前の入力を求められます。 これらのストアド プロシージャの名前を、 Products_Select
、 Products_Insert
、 Products_Update
、および Products_Delete
に変更します。
図 8: ストアド プロシージャの名前を変更する (フルサイズの画像を表示する] をクリックします)
TableAdapter ウィザードが 4 つのストアド プロシージャの作成に使用する T-SQL を表示するには、[SQL スクリプトのプレビュー] ボタンをクリックします。 [SQL スクリプトのプレビュー] ダイアログ ボックスで、スクリプトをファイルに保存したり、クリップボードにコピーしたりできます。
図 9: ストアド プロシージャの生成に使用される SQL スクリプトをプレビューする
ストアド プロシージャに名前を付けた後、[次へ] をクリックして TableAdapter の対応するメソッドに名前を付けます。 アドホック SQL ステートメントを使用する場合と同様に、既存の DataTable を満たすメソッドを作成したり、新しい DataTable を返したりできます。 TableAdapter に、レコードの挿入、更新、および削除のための DB-Direct パターンを含める必要があるかどうかを指定することもできます。 3 つのチェック ボックスはすべてオンのままにしますが、[DataTable を返す] メソッドの名前を GetProducts
に変更します (図 10 を参照)。
図 10: メソッドの Fill
と GetProducts
に名前を付ける (フルサイズの画像を表示する をクリックします)。
[次へ] をクリックすると、ウィザードで実行される手順の概要が表示されます。 [完了] ボタンをクリックしてウィザードを完了します。 ウィザードが完了すると、DataSet のデザイナーに戻り、そこにはProductsDataTable
が含まれているはずです。
図 11: DataSet のデザイナーは、新しく追加された ProductsDataTable
を示しています (フルサイズの画像を表示する をクリックします)。
手順 3: 新しく作成されたストアド プロシージャを調べる
手順 2 で使用した TableAdapter ウィザードでは、データの選択、挿入、更新、および削除のためのストアド プロシージャが自動的に作成されます。 これらのストアド プロシージャは、サーバー エクスプローラーに移動し、データベースのストアド プロシージャ フォルダーにドリルダウンすることで、Visual Studio で表示または変更できます。 図 12 に示すように、Northwind データベースには、 Products_Delete
、 Products_Insert
、 Products_Select
、 Products_Update
の 4 つの新しいストアド プロシージャが含まれています。
図 12: 手順 2 で作成された 4 つのストアド プロシージャは、データベースのストアド プロシージャ フォルダーにあります
注
サーバー エクスプローラーが表示されない場合は、[表示] メニューに移動し、[サーバー エクスプローラー] オプションを選択します。 手順 2 で追加された製品関連のストアド プロシージャが表示されない場合は、[ストアド プロシージャ] フォルダーを右クリックし、[最新の情報に更新] を選択してみてください。
ストアド プロシージャを表示または変更するには、サーバー エクスプローラーでその名前をダブルクリックするか、ストアド プロシージャを右クリックして [開く] を選択します。 図 13 は、 Products_Delete
ストアド プロシージャを開いたときに示しています。
図 13: ストアド プロシージャを開き、Visual Studio 内から変更できます (フルサイズの画像を表示する をクリックします)。
Products_Delete
ストアド プロシージャと Products_Select
ストアド プロシージャの両方の内容は非常に簡単です。 一方、Products_Insert
ストアド プロシージャとProducts_Update
ストアド プロシージャは、SELECT
ステートメントとINSERT
ステートメントの後にUPDATE
ステートメントを実行するため、より詳細な検査が必要です。 たとえば、次の SQL は、 Products_Insert
ストアド プロシージャを構成します。
ALTER PROCEDURE dbo.Products_Insert
(
@ProductName nvarchar(40),
@SupplierID int,
@CategoryID int,
@QuantityPerUnit nvarchar(20),
@UnitPrice money,
@UnitsInStock smallint,
@UnitsOnOrder smallint,
@ReorderLevel smallint,
@Discontinued bit
)
AS
SET NOCOUNT OFF;
INSERT INTO [Products] ([ProductName], [SupplierID], [CategoryID], [QuantityPerUnit],
[UnitPrice], [UnitsInStock], [UnitsOnOrder], [ReorderLevel], [Discontinued])
VALUES (@ProductName, @SupplierID, @CategoryID, @QuantityPerUnit, @UnitPrice,
@UnitsInStock, @UnitsOnOrder, @ReorderLevel, @Discontinued);
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit, UnitPrice,
UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = SCOPE_IDENTITY())
ストアド プロシージャは、TableAdapter ウィザードで指定されたProducts
クエリによって返されたSELECT
列を入力パラメーターとして受け取り、これらの値はINSERT
ステートメントで使用されます。
INSERT
ステートメントの後に、SELECT
クエリを使用して、新しく追加されたレコードのProducts
列の値 (ProductID
を含む) を返します。 この更新機能は、新しく追加された ProductRow
インスタンスをデータベースによって割り当てられた自動インクリメント値でプロパティ ProductID
自動的に更新する際に、バッチ更新パターンを使用して新しいレコードを追加する場合に便利です。
次のコードは、この機能を示しています。
ProductsTableAdapter
型の Typed DataSet 用に作成されたProductsDataTable
およびNorthwindWithSprocs
が含まれています。 新しい製品をデータベースに追加するには、 ProductsRow
インスタンスを作成し、その値を指定し、TableAdapter の Update
メソッドを呼び出して、 ProductsDataTable
を渡します。 内部的には、TableAdapter の Update
メソッドは、渡された DataTable 内の ProductsRow
インスタンスを列挙し (この例では、追加したインスタンスが 1 つだけ)、適切な挿入、更新、または削除コマンドを実行します。 この場合、 Products_Insert
ストアド プロシージャが実行され、 Products
テーブルに新しいレコードが追加され、新しく追加されたレコードの詳細が返されます。 その後、 ProductsRow
インスタンスの ProductID
値が更新されます。
Update
メソッドが完了すると、ProductID
のProductsRow
プロパティを使用して、新しく追加されたレコードのProductID
値にアクセスできます。
// Create the ProductsTableAdapter and ProductsDataTable
NorthwindWithSprocsTableAdapters.ProductsTableAdapter productsAPI =
new NorthwindWithSprocsTableAdapters.ProductsTableAdapter();
NorthwindWithSprocs.ProductsDataTable products =
new NorthwindWithSprocs.ProductsDataTable();
// Create a new ProductsRow instance and set its properties
NorthwindWithSprocs.ProductsRow product = products.NewProductsRow();
product.ProductName = "New Product";
product.CategoryID = 1; // Beverages
product.Discontinued = false;
// Add the ProductsRow instance to the DataTable
products.AddProductsRow(product);
// Update the DataTable using the Batch Update pattern
productsAPI.Update(products);
// At this point, we can determine the value of the newly-added record's ProductID
int newlyAddedProductIDValue = product.ProductID;
同様に、Products_Update
ストアド プロシージャには、SELECT
ステートメントの後にUPDATE
ステートメントが含まれます。
ALTER PROCEDURE dbo.Products_Update
(
@ProductName nvarchar(40),
@SupplierID int,
@CategoryID int,
@QuantityPerUnit nvarchar(20),
@UnitPrice money,
@UnitsInStock smallint,
@UnitsOnOrder smallint,
@ReorderLevel smallint,
@Discontinued bit,
@Original_ProductID int,
@ProductID int
)
AS
SET NOCOUNT OFF;
UPDATE [Products]
SET [ProductName] = @ProductName, [SupplierID] = @SupplierID,
[CategoryID] = @CategoryID, [QuantityPerUnit] = @QuantityPerUnit,
[UnitPrice] = @UnitPrice, [UnitsInStock] = @UnitsInStock,
[UnitsOnOrder] = @UnitsOnOrder, [ReorderLevel] = @ReorderLevel,
[Discontinued] = @Discontinued
WHERE (([ProductID] = @Original_ProductID));
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = @ProductID)
このストアド プロシージャには、 ProductID
の 2 つの入力パラメーター ( @Original_ProductID
と @ProductID
) が含まれていることに注意してください。 この機能により、主キーが変更される可能性があるシナリオが可能になります。 たとえば、従業員データベースでは、各従業員レコードが従業員の社会保障番号を主キーとして使用する場合があります。 既存の従業員の社会保障番号を変更するには、新しい社会保障番号と元の社会保障番号の両方を指定する必要があります。
Products
テーブルでは、ProductID
列はIDENTITY
列であるため、このような機能は必要ありません。変更しないでください。 実際、UPDATE
ストアド プロシージャの Products_Update
ステートメントには、列リストにProductID
列は含まれません。 そのため、 @Original_ProductID
は UPDATE
ステートメントの WHERE
句で使用されますが、 Products
テーブルでは余分であり、 @ProductID
パラメーターに置き換えることができます。 ストアド プロシージャのパラメーターを変更するときは、そのストアド プロシージャを使用する TableAdapter メソッドも更新することが重要です。
手順 4: ストアド プロシージャのパラメーターの変更と TableAdapter の更新
@Original_ProductID
パラメーターは余分であるため、Products_Update
ストアド プロシージャから完全に削除します。
Products_Update
ストアド プロシージャを開き、@Original_ProductID
パラメーターを削除し、WHERE
ステートメントのUPDATE
句で、使用するパラメーター名を @Original_ProductID
から @ProductID
に変更します。 これらの変更を行った後、ストアド プロシージャ内の T-SQL は次のようになります。
ALTER PROCEDURE dbo.Products_Update
(
@ProductName nvarchar(40),
@SupplierID int,
@CategoryID int,
@QuantityPerUnit nvarchar(20),
@UnitPrice money,
@UnitsInStock smallint,
@UnitsOnOrder smallint,
@ReorderLevel smallint,
@Discontinued bit,
@ProductID int
)
AS
SET NOCOUNT OFF;
UPDATE [Products] SET [ProductName] = @ProductName, [SupplierID] = @SupplierID,
[CategoryID] = @CategoryID, [QuantityPerUnit] = @QuantityPerUnit,
[UnitPrice] = @UnitPrice, [UnitsInStock] = @UnitsInStock,
[UnitsOnOrder] = @UnitsOnOrder, [ReorderLevel] = @ReorderLevel,
[Discontinued] = @Discontinued
WHERE (([ProductID] = @ProductID));
SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued
FROM Products
WHERE (ProductID = @ProductID)
これらの変更をデータベースに保存するには、ツール バーの [保存] アイコンをクリックするか、Ctrl + S キーを押します。 この時点で、 Products_Update
ストアド プロシージャは @Original_ProductID
入力パラメーターを想定していませんが、TableAdapter はこのようなパラメーターを渡すよう構成されています。 DataSet デザイナーで TableAdapter を選択し、[プロパティ] ウィンドウに移動し、Products_Update
の UpdateCommand
コレクションの省略記号をクリックすると、TableAdapter が Parameters
ストアド プロシージャに送信するパラメーターを確認できます。 これにより、図 14 に示す [パラメーター コレクション エディター] ダイアログ ボックスが表示されます。
図 14: パラメーター コレクション エディターには、 Products_Update
ストアド プロシージャに渡されるパラメーターの一覧が表示されます
このパラメーターは、メンバーの一覧から @Original_ProductID
パラメーターを選択し、[削除] ボタンをクリックするだけで削除できます。
または、デザイナーで TableAdapter を右クリックし、[構成] を選択して、すべてのメソッドに使用されるパラメーターを更新することもできます。 これにより、TableAdapter 構成ウィザードが表示され、ストアド プロシージャが受け取るパラメーターと共に、選択、挿入、更新、削除に使用されるストアド プロシージャが一覧表示されます。 [更新] ドロップダウン リストをクリックすると、 Products_Update
ストアド プロシージャに必要な入力パラメーターが表示されます。このパラメーターには、 @Original_ProductID
が含まれなくなりました (図 15 を参照)。 [完了] をクリックするだけで、TableAdapter で使用されるパラメーター コレクションが自動的に更新されます。
図 15: TableAdapter の構成ウィザードを使用してメソッド パラメーター コレクションを更新することもできます (フルサイズの画像を表示する をクリックします)。
手順 5: 追加の TableAdapter メソッドを追加する
手順 2 で説明したように、新しい TableAdapter を作成するときに、対応するストアド プロシージャを自動的に生成することは簡単です。 TableAdapter にメソッドを追加する場合も同様です。 これを説明するために、手順 2 で作成したGetProductByProductID(productID)
に ProductsTableAdapter
メソッドを追加します。 このメソッドは、入力として ProductID
値を受け取り、指定された製品に関する詳細を返します。
まず、TableAdapter を右クリックし、コンテキスト メニューから [クエリの追加] を選択します。
図 16: TableAdapter に新しいクエリを追加する
これにより、TableAdapter クエリ構成ウィザードが起動します。このウィザードでは、最初に TableAdapter がデータベースにアクセスする方法を確認するプロンプトが表示されます。 新しいストアド プロシージャを作成するには、[新しいストアド プロシージャの作成] オプションを選択し、[次へ] をクリックします。
図 17: 新しいストアド プロシージャの作成オプションを選択します (フルサイズの画像を表示する をクリックします)。
次の画面では、実行するクエリの種類、一連の行または単一のスカラー値を返すか、 UPDATE
、 INSERT
、または DELETE
ステートメントを実行するかを識別するように求められます。
GetProductByProductID(productID)
メソッドは行を返すので、行を返す SELECT オプションを選択したままにし、[次へ] をクリックします。
図 18: 行オプションを返す SELECT を選択します (フルサイズの画像を表示する をクリックします)
次の画面には、TableAdapter のメイン クエリが表示されます。このクエリには、ストアド プロシージャの名前 (dbo.Products_Select
) が一覧表示されます。 ストアド プロシージャ名を次の SELECT
ステートメントに置き換えます。このステートメントは、指定した製品のすべての製品フィールドを返します。
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued
FROM Products
WHERE ProductID = @ProductID
図 19: ストアド プロシージャ名を SELECT
クエリに置き換える (フルサイズの画像を表示する をクリックします)
次の画面では、作成されるストアド プロシージャの名前を指定するように求められます。
Products_SelectByProductID
名前を入力し、[次へ] をクリックします。
図 20: 新しいストアド プロシージャの Products_SelectByProductID
に名前を付ける (フルサイズの画像を表示する をクリックします)
ウィザードの最後の手順では、生成されたメソッド名を変更したり、[データテーブルの塗りつぶし] パターンを使用するか、DataTable パターンを返すか、またはその両方を使用するかを指定したりできます。 このメソッドでは、両方のオプションをオンのままにしますが、メソッドの名前を FillByProductID
と GetProductByProductID
に変更します。 [次へ] をクリックしてウィザードが実行する手順の概要を表示し、[完了] をクリックしてウィザードを完了します。
図 21: TableAdapter メソッドの名前を FillByProductID
および GetProductByProductID
に変更する (フルサイズの画像を表示する をクリックします)
ウィザードが完了すると、TableAdapter には新しいメソッドが用意されています。 GetProductByProductID(productID)
呼び出されると、先ほど作成した Products_SelectByProductID
ストアド プロシージャが実行されます。 ストアド プロシージャ フォルダーをドリルインし、 Products_SelectByProductID
を開いてサーバー エクスプローラーからこの新しいストアド プロシージャを表示します (表示されない場合は、[ストアド プロシージャ] フォルダーを右クリックして [更新] を選択します)。
SelectByProductID
ストアド プロシージャは入力パラメーターとして@ProductID
を受け取り、ウィザードで入力したSELECT
ステートメントを実行します。
ALTER PROCEDURE dbo.Products_SelectByProductID
(
@ProductID int
)
AS
SET NOCOUNT ON;
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued
FROM Products
WHERE ProductID = @ProductID
手順 6: ビジネス ロジック レイヤー クラスの作成
チュートリアル シリーズ全体を通して、プレゼンテーション層がビジネス ロジック 層 (BLL) へのすべての呼び出しを行う階層構造のアーキテクチャを維持するよう努めていました。 この設計上の決定に従うには、プレゼンテーションレイヤーから製品データにアクセスする前に、新しい型指定されたデータセットの BLL クラスを作成する必要があります。
ProductsBLLWithSprocs.cs
フォルダーに ~/App_Code/BLL
という名前の新しいクラス ファイルを作成し、それに次のコードを追加します。
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindWithSprocsTableAdapters;
[System.ComponentModel.DataObject]
public class ProductsBLLWithSprocs
{
private ProductsTableAdapter _productsAdapter = null;
protected ProductsTableAdapter Adapter
{
get
{
if (_productsAdapter == null)
_productsAdapter = new ProductsTableAdapter();
return _productsAdapter;
}
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, true)]
public NorthwindWithSprocs.ProductsDataTable GetProducts()
{
return Adapter.GetProducts();
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public NorthwindWithSprocs.ProductsDataTable GetProductByProductID(int productID)
{
return Adapter.GetProductByProductID(productID);
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Insert, true)]
public bool AddProduct
(string productName, int? supplierID, int? categoryID,
string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
short? unitsOnOrder, short? reorderLevel, bool discontinued)
{
// Create a new ProductRow instance
NorthwindWithSprocs.ProductsDataTable products =
new NorthwindWithSprocs.ProductsDataTable();
NorthwindWithSprocs.ProductsRow product = products.NewProductsRow();
product.ProductName = productName;
if (supplierID == null)
product.SetSupplierIDNull();
else
product.SupplierID = supplierID.Value;
if (categoryID == null)
product.SetCategoryIDNull();
else
product.CategoryID = categoryID.Value;
if (quantityPerUnit == null)
product.SetQuantityPerUnitNull();
else
product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null)
product.SetUnitPriceNull();
else
product.UnitPrice = unitPrice.Value;
if (unitsInStock == null)
product.SetUnitsInStockNull();
else
product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null)
product.SetUnitsOnOrderNull();
else
product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null)
product.SetReorderLevelNull();
else
product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Add the new product
products.AddProductsRow(product);
int rowsAffected = Adapter.Update(products);
// Return true if precisely one row was inserted, otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateProduct
(string productName, int? supplierID, int? categoryID, string quantityPerUnit,
decimal? unitPrice, short? unitsInStock, short? unitsOnOrder,
short? reorderLevel, bool discontinued, int productID)
{
NorthwindWithSprocs.ProductsDataTable products =
Adapter.GetProductByProductID(productID);
if (products.Count == 0)
// no matching record found, return false
return false;
NorthwindWithSprocs.ProductsRow product = products[0];
product.ProductName = productName;
if (supplierID == null)
product.SetSupplierIDNull();
else
product.SupplierID = supplierID.Value;
if (categoryID == null)
product.SetCategoryIDNull();
else
product.CategoryID = categoryID.Value;
if (quantityPerUnit == null)
product.SetQuantityPerUnitNull();
else
product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null)
product.SetUnitPriceNull();
else
product.UnitPrice = unitPrice.Value;
if (unitsInStock == null)
product.SetUnitsInStockNull();
else
product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null)
product.SetUnitsOnOrderNull();
else
product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null)
product.SetReorderLevelNull();
else
product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// Update the product record
int rowsAffected = Adapter.Update(product);
// Return true if precisely one row was updated, otherwise false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteProduct(int productID)
{
int rowsAffected = Adapter.Delete(productID);
// Return true if precisely one row was deleted, otherwise false
return rowsAffected == 1;
}
}
このクラスは、前のチュートリアルのProductsBLL
クラス セマンティクスを模倣しますが、ProductsTableAdapter
DataSet のProductsDataTable
オブジェクトとNorthwindWithSprocs
オブジェクトを使用します。 たとえば、using NorthwindTableAdapters
のようにクラス ファイルの先頭にProductsBLL
ステートメントを含めるのではなく、ProductsBLLWithSprocs
クラスはusing NorthwindWithSprocsTableAdapters
を使用します。 同様に、このクラスで使用される ProductsDataTable
オブジェクトと ProductsRow
オブジェクトには、 NorthwindWithSprocs
名前空間のプレフィックスが付けられます。
ProductsBLLWithSprocs
クラスには、GetProducts
とGetProductByProductID
の 2 つのデータ アクセス メソッドと、1 つの製品インスタンスを追加、更新、削除するメソッドが用意されています。
手順 7: プレゼンテーション レイヤーからNorthwindWithSprocs
DataSet を操作する
この時点で、ストアド プロシージャを使用して基になるデータベース データにアクセスして変更する DAL を作成しました。 また、すべての製品または特定の製品を取得するメソッドと、製品を追加、更新、削除するためのメソッドを含む基本的な BLL も構築されています。 このチュートリアルを締めくくるために、レコードの表示、更新、削除にBLLのProductsBLLWithSprocs
クラスを使用するASP.NETページを作成します。
NewSprocs.aspx
フォルダーのAdvancedDAL
ページを開き、ツールボックスからデザイナーに GridView をドラッグし、Products
という名前を付けます。 GridView のスマート タグから、 ProductsDataSource
という名前の新しい ObjectDataSource にバインドすることを選択します。 図 22 に示すように、 ProductsBLLWithSprocs
クラスを使用するように ObjectDataSource を構成します。
図 22: ProductsBLLWithSprocs
クラスを使用するように ObjectDataSource を構成する (フルサイズの画像を表示する をクリックします)。
[選択] タブのドロップダウン リストには、 GetProducts
と GetProductByProductID
の 2 つのオプションがあります。 GridView にすべての製品を表示するため、 GetProducts
メソッドを選択します。 UPDATE タブ、INSERT タブ、DELETE タブのドロップダウン リストには、それぞれ 1 つのメソッドしかありません。 これらの各ドロップダウン リストで適切な方法が選択されていることを確認し、[完了] をクリックします。
ObjectDataSource ウィザードが完了すると、Visual Studio によって、製品データ フィールドの GridView に BoundFields と CheckBoxField が追加されます。 GridView の組み込みの編集機能と削除機能を有効にするには、スマート タグに存在する [編集を有効にする] オプションと [削除を有効にする] オプションをオンにします。
図 23: ページには、編集と削除のサポートが有効になっている GridView が含まれています (フルサイズの画像を表示する をクリックします)。
前のチュートリアルで説明したように、ObjectDataSource のウィザードが完了すると、Visual Studio によって OldValuesParameterFormatString
プロパティがoriginal_{0}に設定されます。 BLL のメソッドで想定されるパラメーターを指定して、データ変更機能が適切に機能するためには、この値を既定値の {0} に戻す必要があります。 したがって、 OldValuesParameterFormatString
プロパティを {0} に設定するか、宣言構文からプロパティを完全に削除してください。
データ ソースの構成ウィザードを完了し、GridView でのサポートの編集と削除を有効にし、ObjectDataSource の OldValuesParameterFormatString
プロパティを既定値に戻すと、ページの宣言型マークアップは次のようになります。
<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:BoundField DataField="ProductID" HeaderText="ProductID"
InsertVisible="False" ReadOnly="True"
SortExpression="ProductID" />
<asp:BoundField DataField="ProductName" HeaderText="ProductName"
SortExpression="ProductName" />
<asp:BoundField DataField="SupplierID" HeaderText="SupplierID"
SortExpression="SupplierID" />
<asp:BoundField DataField="CategoryID" HeaderText="CategoryID"
SortExpression="CategoryID" />
<asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit"
SortExpression="QuantityPerUnit" />
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice"
SortExpression="UnitPrice" />
<asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock"
SortExpression="UnitsInStock" />
<asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder"
SortExpression="UnitsOnOrder" />
<asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel"
SortExpression="ReorderLevel" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
DeleteMethod="DeleteProduct" InsertMethod="AddProduct"
SelectMethod="GetProducts" TypeName="ProductsBLLWithSprocs"
UpdateMethod="UpdateProduct">
<DeleteParameters>
<asp:Parameter Name="productID" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="supplierID" Type="Int32" />
<asp:Parameter Name="categoryID" Type="Int32" />
<asp:Parameter Name="quantityPerUnit" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="unitsInStock" Type="Int16" />
<asp:Parameter Name="unitsOnOrder" Type="Int16" />
<asp:Parameter Name="reorderLevel" Type="Int16" />
<asp:Parameter Name="discontinued" Type="Boolean" />
<asp:Parameter Name="productID" Type="Int32" />
</UpdateParameters>
<InsertParameters>
<asp:Parameter Name="productName" Type="String" />
<asp:Parameter Name="supplierID" Type="Int32" />
<asp:Parameter Name="categoryID" Type="Int32" />
<asp:Parameter Name="quantityPerUnit" Type="String" />
<asp:Parameter Name="unitPrice" Type="Decimal" />
<asp:Parameter Name="unitsInStock" Type="Int16" />
<asp:Parameter Name="unitsOnOrder" Type="Int16" />
<asp:Parameter Name="reorderLevel" Type="Int16" />
<asp:Parameter Name="discontinued" Type="Boolean" />
</InsertParameters>
</asp:ObjectDataSource>
この時点で、検証を含むように編集インターフェイスをカスタマイズし、 CategoryID
列と SupplierID
列を DropDownLists としてレンダリングすることで、GridView を整理できました。 また、[削除] ボタンにクライアント側の確認を追加することもできます。これらの機能強化を実装する時間を取ることをお勧めします。 ただし、これらのトピックは前のチュートリアルで取り上げられましたので、ここでは二度と取り上げません。
GridView を強化するかどうかに関係なく、ブラウザーでページのコア機能をテストします。 図 24 に示すように、ページには、行ごとの編集および削除機能を提供する GridView の製品が一覧表示されます。
図 24: GridView から製品を表示、編集、および削除できます (フルサイズの画像を表示する をクリックします)。
概要
型指定された DataSet の TableAdapters は、アドホック SQL ステートメントまたはストアド プロシージャを使用して、データベースからデータにアクセスできます。 ストアド プロシージャを使用する場合は、既存のストアド プロシージャを使用するか、TableAdapter ウィザードを使用して、 SELECT
クエリに基づいて新しいストアド プロシージャを作成するように指示できます。 このチュートリアルでは、ストアド プロシージャを自動的に作成する方法について説明しました。
ストアド プロシージャを自動生成すると時間の節約に役立ちますが、ウィザードによって作成されたストアド プロシージャが、独自に作成したストアド プロシージャと一致しない場合があります。 1 つの例として、Products_Update
パラメーターが余分であった場合でも、@Original_ProductID
と@ProductID
の両方の入力パラメーターが必要な@Original_ProductID
ストアド プロシージャがあります。
多くのシナリオでは、ストアド プロシージャが既に作成されている場合や、ストアド プロシージャのコマンドを細かく制御できるように手動で作成したい場合があります。 どちらの場合も、TableAdapter に、メソッドに既存のストアド プロシージャを使用するように指示します。 これを実現する方法については、次のチュートリアルで説明します。
プログラミングに満足!
もっと読む
この記事で説明したトピックの詳細については、次のリソースを参照してください。
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジを使用しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・セルフ ASP.NET 24時間で2.0です。 彼には mitchell@4GuysFromRolla.comで連絡できます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、ヒルトン ガイゼノーでした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.comにメッセージを送ってください。