データ ソース コントロール
作成者: Microsoft
ASP.NET 1.x の DataGrid コントロールは、Web アプリケーションでのデータ アクセスが大幅に向上しました。 しかし、以前よりも使いにくくなりました。 数多くの便利な機能を利用するには、あいかわらず大量のコードが必要でした。 1.x でのデータ アクセスへの取り組みでは常に、このようなモデルを使います。
ASP.NET 1.x の DataGrid コントロールは、Web アプリケーションでのデータ アクセスが大幅に向上しました。 しかし、以前よりも使いにくくなりました。 数多くの便利な機能を利用するには、あいかわらず大量のコードが必要でした。 1.x でのデータ アクセスへの取り組みでは常に、このようなモデルを使います。
ASP.NET 2.0 では、データ ソース コントロールの一部でこれに対処しています。 ASP.NET 2.0 のデータ ソース コントロールによって、データの取得、データの表示、データの編集のための宣言型モデルが開発者に提供されています。 データ ソース コントロールの目的は、データのソースに関係なく、データ バインド コントロールに対して一貫したデータ表現を提供することです。 ASP.NET 2.0 のデータ ソース コントロールの中心には、DataSourceControl 抽象クラスがあります。 DataSourceControl クラスは、IDataSource インターフェイスと IListSource インターフェイスの基本実装を備えています。後者を使用する場合は、データ ソース コントロールをデータ バインド コントロールの DataSource として割り当て (後述の新しい DataSourceId プロパティを使います)、そこにあるデータをリストとして公開することができます。 データ ソース コントロールのデータの各リストは、DataSourceView オブジェクトとして公開されます。 DataSourceView インスタンスへのアクセスは、IDataSource インターフェイスによって提供されます。 たとえば、GetViewNames メソッドでは、特定のデータ ソース コントロールに関連付けられている DataSourceViews を列挙できる ICollection を返し、GetView メソッドでは、特定の DataSourceView インスタンスに名前でアクセスできます。
データ ソース コントロールは、ユーザー インターフェイスを備えていません。 宣言型構文をサポートするとともに、必要に応じてページ状態にアクセスすることができるように、サーバー コントロールとして実装されます。 データ ソース コントロールを使っても、HTML マークアップがクライアントにレンダリングされることはありません。
Note
後で説明するように、キャッシュには、データ ソース コントロールを使う場合には利点もあります。
接続文字列の格納
データ ソースコントロールの構成方法を確認する前に、接続文字列に関する ASP.NET 2.0 の新機能について説明しておく必要があります。 ASP.NET 2.0 には、実行時に動的に読み取り可能な接続文字列を簡単に格納できる新しいセクションが構成ファイルに導入されています。 <connectionStrings> セクションでは、接続文字列を簡単に格納できます。
次のスニペットを使うと、新しい接続文字列が追加されます。
<connectionStrings> <add name="Northwind" connectionString="Data Source=localhost; Integrated Security=SSPI;Initial Catalog=Northwind;" providerName="System.Data.SqlClient" /> </connectionStrings>
Note
<appSettings> セクションと同様に、<connectionStrings> セクションは、構成ファイルの <system.web> セクションの外部に表示されます。
この接続文字列を使うには、サーバー コントロールの ConnectionString 属性を設定するときに、次の構文を使います。
ConnectionString="<%$ ConnectionStrings:Northwind%>"
また、<connectionStrings> セクションは、機密情報が公開されないように暗号化することもできます。 この機能については、後のモジュールで説明します。
データ ソースのキャッシュ
各 DataSourceControl には、キャッシュを構成するための、EnableCaching、CacheDuration、CacheExpirationPolicy、CacheKeyDependency という 4 つのプロパティが用意されています。
EnableCaching
EnableCaching は、データ ソース コントロールに対してキャッシュが有効かどうかを決定するブール型プロパティです。
CacheDuration プロパティ
CacheDuration プロパティでは、キャッシュを有効にしておく秒数を設定します。 このプロパティを 0 に設定すると、キャッシュは、明示的に無効になるまで有効なままになります。
CacheExpirationPolicy プロパティ
CacheExpirationPolicy プロパティは、[絶対] または [スライド] に設定できます。 [絶対] に設定すると、データがキャッシュされる最大時間は、CacheDuration プロパティによって指定された秒数になります。 [スライド] に設定すると、各操作の実行時に有効期限がリセットされます。
CacheKeyDependency プロパティ
CacheKeyDependency プロパティに文字列値が指定されている場合、ASP.NET では、その文字列に基づいて新しいキャッシュ依存関係が設定されます。 これにより、CacheKeyDependency を変更または削除するだけで、キャッシュを明示的に無効にすることができます。
重要: 偽装が有効になっており、データ ソースやデータのコンテンツへのアクセスがクライアント ID に基づいている場合は、EnableCaching を False に設定してキャッシュを無効にすることをお勧めします。 このシナリオでキャッシュが有効になっており、最初にデータを要求したユーザー以外のユーザーが要求を発行した場合、データ ソースへの承認は適用されません。 データがキャッシュから提供されるのみです。
SqlDataSource コントロール
SqlDataSource コントロールを使うと、開発者は、ADO.NET をサポートするリレーショナル データベースに格納されているデータにアクセスできます。 ここでは、System.Data.SqlClient プロバイダーを使って、SQL Server データベース、System.Data.OleDb プロバイダー、System.Data.Odbc プロバイダー、または System.Data.OracleClient プロバイダーにアクセスして、Oracle にアクセスできます。 したがって、SqlDataSource は、SQL Server データベース内のデータにアクセスするためだけに使うのではありません。
SqlDataSource を使うには、ConnectionString プロパティの値を入力して、SQL コマンドまたはストアド プロシージャを指定するのみです。 SqlDataSource コントロールを使うと、基になる ADO.NET アーキテクチャを操作できます。 これにより、接続が開き、データ ソースに対してクエリが実行されるかストアド プロシージャが実行され、データが返されて、接続が閉じます。
Note
DataSourceControl クラスを使うと接続が自動的に閉じるため、データベース接続のリークによって生成された顧客呼び出しの数が減ります。
次のコード スニペットでは、上記のように構成ファイルに格納されている接続文字列を使って、DropDownList コントロールが SqlDataSource コントロールにバインドされます。
<asp:SqlDataSource id="SqlDataSource1" runat="server" DataSourceMode="DataReader" ConnectionString="<%$ ConnectionStrings:Northwind%>" SelectCommand="SELECT EmployeeID, LastName FROM Employees"> </asp:SqlDataSource><asp:DropDownList id="ListBox1" runat="server" DataTextField="LastName" DataValueField="EmployeeID" DataSourceID="SqlDataSource1"> </asp:DropDownList>
上に示すように、SqlDataSource の DataSourceMode プロパティを使うと、データ ソースのモードが指定されます。 上記の例では、DataSourceMode が DataReader に設定されています。 その場合、SqlDataSource では、順方向専用カーソルと読み取り専用カーソルを使って IDataReader オブジェクトを返します。 返されるオブジェクトの指定された型は、使用されるプロバイダーによって制御されます。 この場合は、web.config ファイルの <connectionStrings> セクションで指定されている System.Data.SqlClient プロバイダーを使っています。 したがって、返されるオブジェクトは SqlDataReader 型になります。 DataSet の DataSourceMode 値を指定すると、データをサーバー上の DataSet に格納できます。 このモードでは、並べ替えやページングなどの機能を追加できます。SqlDataSource を GridView コントロールにデータ バインディングしていた場合は、DataSet モードを選択したことになります。 ただし、DropDownList の場合は、DataReader モードを選択するのが適切です。
Note
SqlDataSource または AccessDataSource をキャッシュする場合は、DataSourceMode プロパティを DataSet に設定する必要があります。 DataReader の DataSourceMode を使ってキャッシュを有効にする場合は、例外が発生します。
SqlDataSource プロパティ
次に示すのは、SqlDataSource コントロールのプロパティの一部です。
CancelSelectOnNullParameter
いずれかのパラメータが null の場合に select コマンドを取り消すかどうかを指定するブール値。 既定では True です。
ConflictDetection
複数のユーザーが同時にデータ ソースを更新している可能性がある状況では、ConflictDetection プロパティによって SqlDataSource コントロールの動作が決まります。 このプロパティは、いずれかの ConflictOptions 列挙値に評価されます。 それは、CompareAllValues と OverwriteChanges という値です。 OverwriteChanges に設定されている場合は、データ ソースに最後にデータを書き込むユーザーが、以前の変更をすべて上書きします。 ただし、ConflictDetection プロパティが CompareAllValues に設定されている場合は、SelectCommand によって返される列に対してパラメータが作成され、各列の元の値を保持するパラメータも作成されます。これにより、SelectCommand の実行後に値が変更されたかどうかは、SqlDataSource によって決まります。
DeleteCommand
データベースから行を削除するときに使う SQL 文字列を設定または取得します。 これには、SQL クエリ名またはストアド プロシージャ名を指定できます。
DeleteCommandType
SQL クエリ (Text) またはストアド プロシージャ (StoredProcedure) の delete コマンドの種類を設定または取得します。
DeleteParameters
SqlDataSource コントロールに関連付けられている SqlDataSourceView オブジェクトの DeleteCommand によって使用されるパラメータを返します。
OldValuesParameterFormatString
このプロパティは、ConflictDetection プロパティが CompareAllValues に設定されている場合に、元の値パラメータの形式を指定するために使います。 既定値は {0} であり、元の値パラメータは元のパラメータと同じ名前ということになります。 つまり、フィールド名が EmployeeID の場合、元の値パラメータは @EmployeeID になります。
SelectCommand
データベースからデータを取得するために使う SQL 文字列を設定または取得します。 これには、SQL クエリ名またはストアド プロシージャ名を指定できます。
SelectCommandType
SQL クエリ (Text) またはストアド プロシージャ (StoredProcedure) の select コマンドの種類を設定または取得します。
SelectParameters
SqlDataSource コントロールに関連付けられている SqlDataSourceView オブジェクトの SelectCommand によって使用されるパラメータを返します。
SortParameterName
データ ソース コントロールによって取得されたデータを並べ替えるときに使うストアド プロシージャ パラメータの名前を取得または設定します。 SelectCommandType が StoredProcedure に設定されている場合にのみ有効です。
SqlCacheDependency
SQL Server キャッシュ依存関係で使うデータベースとテーブルを指定する、セミコロンで区切られた文字列。 (SQL キャッシュ依存関係については、後のモジュールで説明します。)
UpdateCommand
データベース内のデータを更新するときに使う SQL 文字列を設定または取得します。 これには、SQL クエリ名またはストアド プロシージャ名を指定できます。
UpdateCommandType
SQL クエリ (Text) またはストアド プロシージャ (StoredProcedure) の update コマンドの種類を設定または取得します。
UpdateParameters
SqlDataSource コントロールに関連付けられている SqlDataSourceView オブジェクトの UpdateCommand によって使用さいるパラメータを返します。
AccessDataSource コントロール
AccessDataSource コントロールは、SqlDataSource クラスから派生し、Microsoft Access データベースへのデータ バインドに使います。 AccessDataSource コントロールの ConnectionString プロパティは、読み取り専用プロパティです。 次に示すように、ConnectionString プロパティを使う代わりに、DataFile プロパティを使って、Access Database をポイントします。
<asp:AccessDataSource id="AccessDataSource1" runat="server" DataFile="~/App_Data/Northwind.mdb"> </asp:AccessDataSource>
AccessDataSource を使うと、ベース SqlDataSource の ProviderName が常に System.Data.OleDb に設定されます。また、AccessDataSource は、Microsoft.Jet.OLEDB.4.0 OLE DB プロバイダーを使ってデータベースに接続します。 AccessDataSource コントロールは、パスワードで保護された Access データベースへの接続には使うことができません。 パスワードで保護されたデータベースに接続する必要がある場合は、SqlDataSource コントロールを使う必要があります。
Note
Web サイト内に格納されている Access データベースは、App_Data ディレクトリに配置する必要があります。 ASP.NET では、このディレクトリ内のファイルを参照できません。 Access データベースを使う場合は、App_Data ディレクトリへの読み取りアクセス許可と書き込みアクセス許可をプロセス アカウントに付与する必要があります。
XmlDataSource コントロール
XmlDataSource は、XML データをデータ バインド コントロールにデータ バインドするために使います。 DataFile プロパティを使って XML ファイルにバインドすることも、Data プロパティを使って XML 文字列にバインドすることもできます。 XmlDataSource を使うと、バインド可能なフィールドとして XML 属性が公開されます。 属性として表されていない値にバインドする必要がある場合は、XSL 変換を使う必要があります。 XML データをフィルター処理するには、XPath 式を使うこともできます。
次の XML ファイルについて考えてみましょう。
<?xml version="1.0" encoding="utf-8" ?> <People> <Person FirstName="Jake" LastName="Stone"> <Address> <Street>345 Maple St.</Street> <City>Redmond</City> <Region>WA</Region> <ZipCode>01434</ZipCode> </Address> <Job> <Title>CEO</Title> <Description>Develops company strategies.</Description> </Job> </Person> <Person FirstName="Jacob" LastName="Ladder"> <Address> <Street>123 Elm St.</Street> <City>Seattle</City> <Region>WA</Region> <ZipCode>11223</ZipCode> </Address> <Job> <Title>Attorney</Title> <Description>Reviews legal issues.</Description> </Job> </Person> <Person FirstName="Angela" LastName="Hound"> <Address> <Street>34 Palm Avenue</Street> <City>Renton</City> <Region>WA</Region> <ZipCode>63910</ZipCode> </Address> <Job> <Title>IT Director</Title> <Description>In charge of corporate network.</Description> </Job> </Person> </People>
XmlDataSource では、<Person> ノードのみをフィルター処理するために、People/Person の XPath プロパティが使用されていることに注意してください。 次に、DataTextField プロパティを使って、DropDownList を LastName 属性にデータ バインドします。
XmlDataSource コントロールは主に読み取り専用 XML データへのデータ バインドに使いますが、XML データ ファイルの編集も可能です。 このような場合、XML ファイル内の情報の自動挿入、更新、削除は、他のデータ ソース コントロールと同様に、自動的に行われることはありません。 代わりに、XmlDataSource コントロールの次のメソッドを使って、データを手動で編集するコードを記述する必要があります。
GetXmlDocument
XmlDataSource によって取得された XML コードを含む XmlDocument オブジェクトを取得します。
および
メモリ内の XmlDocument をデータ ソースに戻して保存します。
Save メソッドは次の 2 つの条件が満たされた場合にのみ機能することを認識しておくことが重要です。
- XmlDataSource では、Data プロパティを使ってメモリ内の XML データにバインドするのではなく、DataFile プロパティを使って XML ファイルにバインドします。
- Transform プロパティまたは TransformFile プロパティを使って変換が指定されることはありません。
複数のユーザーによって同時に呼び出されると、Save メソッドが予期しない結果をもたらす可能性があることにも注意してください。
ObjectDataSource コントロール
これまでに説明してきたデータ ソース コントロールは、データ ソース コントロールがデータ ストアと直接通信する 2 層アプリケーションにとっては最適な選択です。 ただし、多くの実際のアプリケーションは多層アプリケーションであり、そこではデータ ソース コントロールがビジネス オブジェクトと通信する必要がありますが、一方でビジネス オブジェクトはデータ層と通信します。 このような状況では、ObjectDataSource が非常に適しています。 ObjectDataSource は、ソース オブジェクトと一緒に動作します。 オブジェクトに静的メソッド (Visual Basic で共有) ではなくインスタンス メソッドがある場合は、ObjectDataSource コントロールによって、ソース オブジェクトのインスタンスの作成、指定したメソッドの呼び出し、1 つの要求のスコープ内でのオブジェクト インスタンスの破棄が行われます。 そのため、オブジェクトはステートレスである必要があります。 つまり、オブジェクトを使って、1 つの要求のスパン内で必要なすべてのリソースを取得して解放する必要があります。 ソース オブジェクトの作成方法を制御するには、ObjectDataSource コントロールの ObjectCreating イベントを処理します。 ソース オブジェクトのインスタンスを作成して、ObjectDataSourceEventArgs クラスの ObjectInstance プロパティをそのインスタンスに設定します。 ObjectDataSource コントロールでは、独自のインスタンスを作成する代わりに、ObjectCreating イベントに作成されたインスタンスを使います。
ObjectDataSource コントロールのソース オブジェクトによって、データの取得と変更のために呼び出すことができるパブリック静的メソッド (Visual Basic で共有) が公開される場合は、ObjectDataSource コントロールによって、そのメソッドが直接呼び出されます。 メソッド呼び出しを行うために ObjectDataSource コントロールを使ってソース オブジェクトのインスタンスを作成する必要がある場合は、そのオブジェクトにはパラメータを受け取らないパブリック コンストラクターを含める必要があります。 ObjectDataSource コントロールでは、ソース オブジェクトの新しいインスタンスが作成されると、このコンストラクターが呼び出されます。
ソース オブジェクトにパラメータのないパブリック コンストラクターが含まれていない場合は、ObjectCreating イベントで ObjectDataSource コントロールによって使用されるソース オブジェクトのインスタンスを作成できます。
オブジェクト メソッドの指定
ObjectDataSource コントロールのソース オブジェクトには、データの選択、挿入、更新、または削除に使うメソッドをいくつでも含めることができます。 これらのメソッドは、ObjectDataSource コントロールの SelectMethod、InsertMethod、UpdateMethod、または DeleteMethod プロパティを使って識別されるメソッドの名前に基づいて、ObjectDataSource コントロールによって呼び出されます。 ソース オブジェクトには、オプションの SelectCount メソッドを含めることもできます。このメソッドは、SelectCountMethod プロパティを使って、データ ソースのオブジェクトの合計数を返す ObjectDataSource コントロールによって識別されます。 ObjectDataSource コントロールを使うと、Select メソッドが呼び出された後に SelectCount メソッドが呼び出されて、ページング時に使うデータ ソースのレコードの合計数を取得できます。
データ ソースコントロールを使用するラボ
演習 1 - SqlDataSource コントロールを使ってデータを表示する
次の演習では、SqlDataSource コントロールを使って Northwind データベースに接続します。 SQL Server 2000 インスタンス上で Northwind データベースにアクセスできるという想定です。
新しい ASP.NET Web サイトを作成します。
新しい web.config ファイルを追加します。
- ソリューション エクスプローラーでプロジェクトを右クリックし、[新しい項目の追加] をクリックします。
- テンプレートの一覧から [Web 構成ファイル] を選択し、[追加] をクリックします。
<connectionStrings> セクションを次のように編集します。
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ConnectionStrings:Northwind%>" SelectCommand="SELECT * FROM Products"> </asp:SqlDataSource>
コード ビューに切り替え、次のように、ConnectionString 属性と SelectCommand 属性を <asp:SqlDataSource> コントロールに追加します。
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ConnectionStrings:Northwind%>" SelectCommand="SELECT * FROM Products"> </asp:SqlDataSource>
デザイン ビューから、新しい GridView コントロールを追加します。
[GridView タスク] メニューの [データ ソースの選択] ドロップダウンで [SqlDataSource1] を選択します。
Default.aspx を右クリックし、メニューから [ブラウザーで表示] を選択します。 保存を求められたら、[はい] をクリックします。
GridView には、Products テーブルのデータが表示されます。
演習 2 - SqlDataSource コントロールを使ってデータを編集する
次の演習では、宣言構文を使って DropDownList コントロールをデータバインドする方法について説明します。ここでは、DropDownList コントロールに表示されるデータを編集できます。
デザイン ビューで、GridView コントロールを Default.aspx から削除します。
重要: SqlDataSource コントロールはページ上に残しておいてください。
DropDownList コントロールを Default.aspx に追加します。
ソース ビューに切り替えます。
次のように、DataSourceId、DataTextField、DataValueField 属性を <asp:DropDownList> コントロールに追加します。
<asp:DropDownList ID="ddlProducts" runat="server" DataSourceId="SqlDataSource1" DataTextField="ProductName" DataValueField="ProductID"> </asp:DropDownList>
Default.aspx を保存し、ブラウザーに表示します。 DropDownList には Northwind データベースのすべての製品が含まれていることに注意してください。
ブラウザーを閉じます。
Default.aspx のソース ビューで、DropDownList コントロールの下に新しい TextBox コントロールを追加します。 TextBox の ID プロパティを txtProductName に変更します。
TextBox コントロールの下に、新しい Button コントロールを追加します。 Button の ID プロパティを btnUpdate に、Text プロパティを [製品名の更新] に変更します。
Default.aspx のソース ビューで、次のように UpdateCommand プロパティと 2 つの新しい UpdateParameters を SqlDataSource タグに追加します。
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ConnectionStrings:Northwind%>" SelectCommand="SELECT * FROM Products" UpdateCommand="UPDATE Products SET ProductName=@ProductName WHERE ProductID=@ProductID"> <UpdateParameters> <asp:ControlParameter Name="ProductName" ControlID="txtProductName" PropertyName="Text" /> <asp:ControlParameter Name="ProductID" ControlID="ddlProducts" PropertyName="SelectedValue" /> </asp:SqlDataSource>
Note
このコードには 2 つの更新パラメータ (ProductName と ProductID) が追加されていることに注意してください。 これらのパラメータは、txtProductName TextBox の Text プロパティと ddlProducts DropDownList の SelectedValue プロパティにマップされます。
デザイン ビューに切り替え、Button コントロールをダブルクリックして、イベント ハンドラーを追加します。
次のコードを btnUpdate_Click コードに追加します。
SqlDataSource1.Update();
Default.aspx を右クリックし、ブラウザーでの表示を選択します。 すべての変更を保存するように求められたら、[はい] をクリックします。
ASP.NET 2.0 の部分クラスを使うと、実行時にコンパイルできます。 コードの変更が有効になっていることを確認する目的で、アプリケーションをビルドする必要はありません。
DropDownList から製品を選択します。
選択した製品の新しい名前を TextBox に入力し、[更新] ボタンをクリックします。
データベースで製品名が更新されます。
演習 3 ObjectDataSource コントロールを使う
この演習では、ObjectDataSource コントロールとソース オブジェクトを使って Northwind データベースを操作する方法について説明します。
ソリューション エクスプローラーでプロジェクトを右クリックし、[新しい項目の追加] をクリックします。
テンプレートの一覧で [Web フォーム] を選択します。 名前を「object.aspx」に変更し、[追加] をクリックします。
ソリューション エクスプローラーでプロジェクトを右クリックし、[新しい項目の追加] をクリックします。
テンプレートの一覧で [クラス] を選択します。 クラスの名前を NorthwindData.cs に変更し、[追加] をクリックします。
App_Code フォルダーにクラスを追加するように求められたら、[はい] をクリックします。
次のコードを NorthwindData.cs ファイルに追加します。
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 System.Data.SqlClient; public class NorthwindData { private string _connectionString; public NorthwindData() { Initialize(); } private void Initialize() { if (ConfigurationManager.ConnectionStrings["Northwind"] == null || ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString.Trim() == "") { throw new Exception("A connection string named 'Northwind' with " + "a valid connection string must exist in the <connectionStrings> " + "configuration section for the application."); } _connectionString = ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString; } public DataTable GetAllEmployees(string sortColumns, int startRecord, int maxRecords) { VerifySortColumns(sortColumns); string sqlCmd = "SELECT EmployeeID, LastName, FirstName, Address, " + "City, Region, PostalCode FROM Employees "; if (sortColumns.Trim() == "") sqlCmd += "ORDER BY EmployeeID"; else sqlCmd += "ORDER BY " + sortColumns; SqlConnection conn = new SqlConnection(_connectionString); SqlDataAdapter da = new SqlDataAdapter(sqlCmd, conn); DataSet ds = new DataSet(); try { conn.Open(); da.Fill(ds, startRecord, maxRecords, "Employees"); } catch (SqlException e) { // Handle exception. } finally { conn.Close(); } return ds.Tables["Employees"]; } public int SelectCount() { SqlConnection conn = new SqlConnection(_connectionString); SqlCommand cmd = new SqlCommand("SELECT COUNT(*) FROM Employees", conn); int result = 0; try { conn.Open(); result = (int)cmd.ExecuteScalar(); } catch (SqlException e) { // Handle exception. } finally { conn.Close(); } return result; } ////////// // Verify that only valid columns are specified in the sort expression to // avoid a SQL Injection attack. private void VerifySortColumns(string sortColumns) { if (sortColumns.ToLowerInvariant().EndsWith(" desc")) sortColumns = sortColumns.Substring(0, sortColumns.Length - 5); string[] columnNames = sortColumns.Split(','); foreach (string columnName in columnNames) { switch (columnName.Trim().ToLowerInvariant()) { case "employeeid": break; case "lastname": break; case "firstname": break; case "": break; default: throw new ArgumentException("SortColumns contains an " + "invalid column name."); break; } } } // Select an employee. public DataTable GetEmployee(int EmployeeID) { SqlConnection conn = new SqlConnection(_connectionString); SqlDataAdapter da = new SqlDataAdapter("SELECT EmployeeID, LastName, FirstName, " + "Address, City, Region, PostalCode " + " FROM Employees WHERE EmployeeID = @EmployeeID", conn); da.SelectCommand.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = EmployeeID; DataSet ds = new DataSet(); try { conn.Open(); da.Fill(ds, "Employees"); } catch (SqlException e) { // Handle exception. } finally { conn.Close(); } return ds.Tables["Employees"]; } // Delete the Employee by ID. public int DeleteEmployee(int EmployeeID) { SqlConnection conn = new SqlConnection(_connectionString); SqlCommand cmd = new SqlCommand("DELETE FROM Employees WHERE " + "EmployeeID = @EmployeeID", conn); cmd.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = EmployeeID; int result = 0; try { conn.Open(); result = cmd.ExecuteNonQuery(); } catch (SqlException e) { // Handle exception. } finally { conn.Close(); } return result; } // Update the Employee by original ID. public int UpdateEmployee(int EmployeeID, string LastName, string FirstName, string Address, string City, string Region, string PostalCode) { if (String.IsNullOrEmpty(FirstName)) throw new ArgumentException("FirstName cannot be null or an empty string."); if (String.IsNullOrEmpty(LastName)) throw new ArgumentException("LastName cannot be null or an empty string."); if (Address == null) { Address = String.Empty; } if (City == null) { City = String.Empty; } if (Region == null) { Region = String.Empty; } if (PostalCode == null) { PostalCode = String.Empty; } SqlConnection conn = new SqlConnection(_connectionString); SqlCommand cmd = new SqlCommand("UPDATE Employees " + " SET FirstName=@FirstName, " + "LastName=@LastName, " + "Address=@Address, City=@City, " + "Region=@Region, " + "PostalCode=@PostalCode " + "WHERE EmployeeID=@EmployeeID", conn); cmd.Parameters.Add("@FirstName", SqlDbType.VarChar, 10).Value = FirstName; cmd.Parameters.Add("@LastName", SqlDbType.VarChar, 20).Value = LastName; cmd.Parameters.Add("@Address", SqlDbType.VarChar, 60).Value = Address; cmd.Parameters.Add("@City", SqlDbType.VarChar, 15).Value = City; cmd.Parameters.Add("@Region", SqlDbType.VarChar, 15).Value = Region; cmd.Parameters.Add("@PostalCode", SqlDbType.VarChar, 10).Value = PostalCode; cmd.Parameters.Add("@EmployeeID", SqlDbType.Int).Value = EmployeeID; int result = 0; try { conn.Open(); result = cmd.ExecuteNonQuery(); } catch (SqlException e) { // Handle exception. } finally { conn.Close(); } return result; } // Insert an Employee. public int InsertEmployee(string LastName, string FirstName, string Address, string City, string Region, string PostalCode) { if (String.IsNullOrEmpty(FirstName)) throw new ArgumentException("FirstName cannot be null or an empty string."); if (String.IsNullOrEmpty(LastName)) throw new ArgumentException("LastName cannot be null or an empty string."); if (Address == null) { Address = String.Empty; } if (City == null) { City = String.Empty; } if (Region == null) { Region = String.Empty; } if (PostalCode == null) { PostalCode = String.Empty; } SqlConnection conn = new SqlConnection(_connectionString); SqlCommand cmd = new SqlCommand("INSERT INTO Employees " + " (FirstName, LastName, Address, " + " City, Region, PostalCode) " + " Values(@FirstName, @LastName, " + "@Address, @City, @Region, @PostalCode); " + "SELECT @EmployeeID = SCOPE_IDENTITY()", conn); cmd.Parameters.Add("@FirstName", SqlDbType.VarChar, 10).Value = FirstName; cmd.Parameters.Add("@LastName", SqlDbType.VarChar, 20).Value = LastName; cmd.Parameters.Add("@Address", SqlDbType.VarChar, 60).Value = Address; cmd.Parameters.Add("@City", SqlDbType.VarChar, 15).Value = City; cmd.Parameters.Add("@Region", SqlDbType.VarChar, 15).Value = Region; cmd.Parameters.Add("@PostalCode", SqlDbType.VarChar, 10).Value = PostalCode; SqlParameter p = cmd.Parameters.Add("@EmployeeID", SqlDbType.Int); p.Direction = ParameterDirection.Output; int newEmployeeID = 0; try { conn.Open(); cmd.ExecuteNonQuery(); newEmployeeID = (int)p.Value; } catch (SqlException e) { // Handle exception. } finally { conn.Close(); } return newEmployeeID; } // // Methods that support Optimistic Concurrency checks. // // Delete the Employee by ID. public int DeleteEmployee(int original_EmployeeID, string original_LastName, string original_FirstName, string original_Address, string original_City, string original_Region, string original_PostalCode) { if (String.IsNullOrEmpty(original_FirstName)) throw new ArgumentException("FirstName cannot be null or an empty string."); if (String.IsNullOrEmpty(original_LastName)) throw new ArgumentException("LastName cannot be null or an empty string."); if (original_Address == null) { original_Address = String.Empty; } if (original_City == null) { original_City = String.Empty; } if (original_Region == null) { original_Region = String.Empty; } if (original_PostalCode == null) { original_PostalCode = String.Empty; } string sqlCmd = "DELETE FROM Employees WHERE EmployeeID = " + @original_EmployeeID SqlConnection conn = new SqlConnection(_connectionString); SqlCommand cmd = new SqlCommand(sqlCmd, conn); cmd.Parameters.Add("@original_EmployeeID", SqlDbType.Int).Value = original_EmployeeID; cmd.Parameters.Add("@original_FirstName", SqlDbType.VarChar, 10).Value = original_FirstName; cmd.Parameters.Add("@original_LastName", SqlDbType.VarChar, 20).Value = original_LastName; cmd.Parameters.Add("@original_Address", SqlDbType.VarChar, 60).Value = original_Address; cmd.Parameters.Add("@original_City", SqlDbType.VarChar, 15).Value = original_City; cmd.Parameters.Add("@original_Region", SqlDbType.VarChar, 15).Value = original_Region; cmd.Parameters.Add("@original_PostalCode", SqlDbType.VarChar, 10).Value = original_PostalCode; int result = 0; try { conn.Open(); result = cmd.ExecuteNonQuery(); } catch (SqlException e) { // Handle exception. } finally { conn.Close(); } return result; } // Update the Employee by original ID. public int UpdateEmployee(string LastName, string FirstName, string Address, string City, string Region, string PostalCode, int original_EmployeeID, string original_LastName, string original_FirstName, string original_Address, string original_City, string original_Region, string original_PostalCode) { if (String.IsNullOrEmpty(FirstName)) throw new ArgumentException("FirstName cannot be null or an empty string."); if (String.IsNullOrEmpty(LastName)) throw new ArgumentException("LastName cannot be null or an empty string."); if (Address == null) { Address = String.Empty; } if (City == null) { City = String.Empty; } if (Region == null) { Region = String.Empty; } if (PostalCode == null) { PostalCode = String.Empty; } if (original_Address == null) { original_Address = String.Empty; } if (original_City == null) { original_City = String.Empty; } if (original_Region == null) { original_Region = String.Empty; } if (original_PostalCode == null) { original_PostalCode = String.Empty; } string sqlCmd = "UPDATE Employees " + " SET FirstName = @FirstName, LastName = @LastName, " + " Address = @Address, City = @City, Region = @Region, " + " PostalCode = @PostalCode " + " WHERE EmployeeID = @original_EmployeeID"; SqlConnection conn = new SqlConnection(_connectionString); SqlCommand cmd = new SqlCommand(sqlCmd, conn); cmd.Parameters.Add("@FirstName", SqlDbType.VarChar, 10).Value = FirstName; cmd.Parameters.Add("@LastName", SqlDbType.VarChar, 20).Value = LastName; cmd.Parameters.Add("@Address", SqlDbType.VarChar, 60).Value = Address; cmd.Parameters.Add("@City", SqlDbType.VarChar, 15).Value = City; cmd.Parameters.Add("@Region", SqlDbType.VarChar, 15).Value = Region; cmd.Parameters.Add("@PostalCode", SqlDbType.VarChar, 10).Value = PostalCode; cmd.Parameters.Add("@original_EmployeeID", SqlDbType.Int).Value = original_EmployeeID; cmd.Parameters.Add("@original_FirstName", SqlDbType.VarChar, 10).Value = original_FirstName; cmd.Parameters.Add("@original_LastName", SqlDbType.VarChar, 20).Value = original_LastName; cmd.Parameters.Add("@original_Address", SqlDbType.VarChar, 60).Value = original_Address; cmd.Parameters.Add("@original_City", SqlDbType.VarChar, 15).Value = original_City; cmd.Parameters.Add("@original_Region", SqlDbType.VarChar, 15).Value = original_Region; cmd.Parameters.Add("@original_PostalCode", SqlDbType.VarChar, 10).Value = original_PostalCode; int result = 0; try { conn.Open(); result = cmd.ExecuteNonQuery(); } catch (SqlException e) { // Handle exception. } finally { conn.Close(); } return result; } }
次のコードを object.aspx のソース ビューに追加します。
<%@ Page language="C#" %> <script RunAt="server"> void EmployeesDetailsView_ItemInserted(Object sender, DetailsViewInsertedEventArgs e) { EmployeesGridView.DataBind(); } void EmployeesDetailsView_ItemUpdated(Object sender, DetailsViewUpdatedEventArgs e) { EmployeesGridView.DataBind(); } void EmployeesDetailsView_ItemDeleted(Object sender, DetailsViewDeletedEventArgs e) { EmployeesGridView.DataBind(); } void EmployeesGridView_OnSelectedIndexChanged(object sender, EventArgs e) { EmployeeDetailsObjectDataSource.SelectParameters["EmployeeID"].DefaultValue = EmployeesGridView.SelectedDataKey.Value.ToString(); EmployeesDetailsView.DataBind(); } void EmployeeDetailsObjectDataSource_OnInserted(object sender, ObjectDataSourceStatusEventArgs e) { EmployeeDetailsObjectDataSource.SelectParameters["EmployeeID"].DefaultValue = e.ReturnValue.ToString(); EmployeesDetailsView.DataBind(); } void EmployeeDetailsObjectDataSource_OnUpdated(object sender, ObjectDataSourceStatusEventArgs e) { if ((int)e.ReturnValue == 0) Msg.Text = "Employee was not updated. Please try again."; } void EmployeeDetailsObjectDataSource_OnDeleted(object sender, ObjectDataSourceStatusEventArgs e) { if ((int)e.ReturnValue == 0) Msg.Text = "Employee was not deleted. Please try again."; } void Page_Load() { Msg.Text = ""; } </script> <html> <body> <form id="Form1" runat="server"> <h3>ObjectDataSource Example</h3> <asp:Label id="Msg" runat="server" ForeColor="Red" /> <asp:ObjectDataSource ID="EmployeesObjectDataSource" runat="server" TypeName="NorthwindData" SortParameterName="SortColumns" EnablePaging="true" SelectCountMethod="SelectCount" StartRowIndexParameterName="StartRecord" MaximumRowsParameterName="MaxRecords" SelectMethod="GetAllEmployees" > </asp:ObjectDataSource> <asp:ObjectDataSource ID="EmployeeDetailsObjectDataSource" runat="server" TypeName="NorthwindData" ConflictDetection="CompareAllValues" OldValuesParameterFormatString="{0}" SelectMethod="GetEmployee" InsertMethod="InsertEmployee" UpdateMethod="UpdateEmployee" DeleteMethod="DeleteEmployee" OnInserted="EmployeeDetailsObjectDataSource_OnInserted" OnUpdated="EmployeeDetailsObjectDataSource_OnUpdated" OnDeleted="EmployeeDetailsObjectDataSource_OnDeleted"> <SelectParameters> <asp:Parameter Name="EmployeeID" Type="Int32" /> </SelectParameters> </asp:ObjectDataSource> <table cellspacing="10"> <tr> <td valign="top"> <asp:GridView ID="EmployeesGridView" DataSourceID="EmployeesObjectDataSource" AutoGenerateColumns="false" AllowSorting="true" AllowPaging="true" PageSize="5" DataKeyNames="EmployeeID" OnSelectedIndexChanged="EmployeesGridView_OnSelectedIndexChanged" RunAt="server"> <HeaderStyle backcolor="lightblue" forecolor="black"/> <Columns> <asp:ButtonField Text="Details..." HeaderText="Show Details" CommandName="Select"/> <asp:BoundField DataField="EmployeeID" HeaderText="Employee ID" SortExpression="EmployeeID" /> <asp:BoundField DataField="FirstName" HeaderText="First Name" SortExpression="FirstName" /> <asp:BoundField DataField="LastName" HeaderText="Last Name" SortExpression="LastName, FirstName" /> </Columns> </asp:GridView> </td> <td valign="top"> <asp:DetailsView ID="EmployeesDetailsView" DataSourceID="EmployeeDetailsObjectDataSource" AutoGenerateRows="false" EmptyDataText="No records." DataKeyNames="EmployeeID" Gridlines="Both" AutoGenerateInsertButton="true" AutoGenerateEditButton="true" AutoGenerateDeleteButton="true" OnItemInserted="EmployeesDetailsView_ItemInserted" OnItemUpdated="EmployeesDetailsView_ItemUpdated" OnItemDeleted="EmployeesDetailsView_ItemDeleted" RunAt="server"> <HeaderStyle backcolor="Navy" forecolor="White"/> <RowStyle backcolor="White"/> <AlternatingRowStyle backcolor="LightGray"/> <EditRowStyle backcolor="LightCyan"/> <Fields> <asp:BoundField DataField="EmployeeID" HeaderText="Employee ID" InsertVisible="False" ReadOnly="true"/> <asp:BoundField DataField="FirstName" HeaderText="First Name"/> <asp:BoundField DataField="LastName" HeaderText="Last Name"/> <asp:BoundField DataField="Address" HeaderText="Address"/> <asp:BoundField DataField="City" HeaderText="City"/> <asp:BoundField DataField="Region" HeaderText="Region"/> <asp:BoundField DataField="PostalCode" HeaderText="Postal Code"/> </Fields> </asp:DetailsView> </td> </tr> </table> </form> </body> </html>
すべてのファイルを保存し、object.aspx を参照します。
詳細の表示、従業員の編集、従業員の追加、従業員の削除を行って、インターフェイスを操作します。