ASP.NET 2.0 の GridView の例: GridView の基になるデータの削除
ここをクリックして TOC に戻ります。
これまでに見てきたすべての例は、読み取り専用 の GridViewです。 この例では、データのドリルダウンやデータの並べ替えやページングが許可されている可能性がありますが、エンド ユーザーはデータのみを表示できました。 ただし、ユーザーが Web アプリケーションを利用するデータを削除または更新できるようにする場合もあります。 データの編集と削除に使用できるさまざまな手法があり、その 1 つは GridView の組み込みの削除および編集機能を利用しています。
データのページングや並べ替えと同様に、 SqlDataSource を使用してデータベースから直接取得されるデータの削除は簡単に実行でき、マウスを数回クリックするだけで済みます。 ただし、ObjectDataSource によって設定された GridView からデータを削除するには、基になるデータ アクセスレイヤー クラスにデータを削除するためのメソッドを提供する必要があります。
このセクションでは、SqlDataSource と ObjectDataSource の両方を利用する GridView からデータを削除する方法について説明します。 また、 GridView の削除機能にクライアント側の確認を追加して、ユーザーがレコードを誤って削除しないようにする方法についても説明します。
SqlDataSource からのデータの削除
[ GridView に表示されるデータのフィルター処理] セクションで、DropDownList から選択した特定の製品の GridView に注文の詳細を表示する方法を確認しました。 このデモを強化して、注文の詳細を削除する機能を含めましょう。
GridView からの削除を有効にするには、SELECT ステートメントに加えて DELETE ステートメントを含むように SqlDataSource を拡張する必要があります。 DELETE ステートメントを追加する最も簡単な方法は、 SqlDataSource のウィザードを使用することです。 手順 2 で、返すテーブルまたはビューとそのフィールドを選択すると、[詳細設定] ボタンが表示され、クリックすると図 35 に示すダイアログ ボックスが表示されます。
図 35
上部のチェック ボックス (Insert、Update、Delete ステートメントの生成) をクリックすると、 SqlDataSource によって、必要な INSERT、UPDATE、DELETE ステートメントと、必要なパラメーター宣言が自動的に作成されます。 図 35 のダイアログ ボックスには、オプティミスティック コンカレンシーを使用する 2 番目のチェック ボックスもあります。 ユーザーが GridView を使用してレコードを更新または削除するときにこのボックスをオンにすると、変更された行のデータが GridView の読み込み後に変更されていないことを確認するために、チェックが作成されます。 レコードが変更された場合、行が一致しないため、更新または削除は失敗します。この場合に実行するアクションのコースを決定するのは、ページ開発者の責任です。 [最適化コンカレンシーを使用する] をオフのままにした場合、更新または削除される行は、主キー以外のフィールドの値を無視して、その主キー値と一致するだけです。
つまり、ユーザーが他のユーザーの変更をステップ実行した場合に、ユーザーの更新または削除が行われないようにするには、この 2 番目のチェック ボックスをチェックします。最後に更新/削除を優先する場合はオフのままにします。 このチェック ボックスをオンまたはオフのままにすると、 SqlDataSource の ConflictDetection プロパティがそれぞれ CompareAllValues または OverwriteChanges に設定されます。 次のセクションで説明するように、 ObjectDataSource には ConflictDetection プロパティも含まれます。このプロパティは、オプティミスティック コンカレンシーを使用するかどうかを指定するために使用されます。
メモGridView を使用した削除をサポートするように DataSource を構成する場合は、基になるデータの主キーを構成するフィールドが SELECT クエリによって返されることを確認してください。これらのフィールドを GridView に表示する予定がない場合でも、 次に示すように、Order Details テーブルからレコードを取得する SqlDataSource の SelectCommand は、複合主キー フィールドである OrderID と ProductID を取り戻しています。 さらに、**GridView**の DataKeyNames プロパティを主キー フィールドに設定する必要があります。 (複数の主キー フィールドがある場合は、それぞれコンマで区切ります)。
最初のチェック ボックスのタイトルが示すように、チェック ボックスをオンにすると、DELETE ステートメントだけでなく、INSERT ステートメントと UPDATE ステートメントも生成されます。 このチェック ボックスをオンにすると、 SqlDataSource の宣言型構文が次のように展開されます。
<asp:SqlDataSource ConnectionString="<%$ ConnectionStrings:NWConnectionString %>" DeleteCommand="DELETE FROM [Order Details] WHERE [OrderID] = @original_OrderID AND [ProductID] = @original_ProductID" ID="SqlDataSource1" InsertCommand="INSERT INTO [Order Details] ([OrderID], [ProductID], [UnitPrice], [Quantity]) VALUES (@OrderID, @ProductID, @UnitPrice, @Quantity)" runat="server" SelectCommand="SELECT [OrderID], [ProductID], [UnitPrice], [Quantity] FROM [Order Details] WHERE ([ProductID] = @ProductID)" UpdateCommand="UPDATE [Order Details] SET [UnitPrice] = @UnitPrice, [Quantity] = @Quantity WHERE [OrderID] = @original_OrderID AND [ProductID] = @original_ProductID"> <DeleteParameters> <asp:Parameter Name="original_OrderID" Type="Int32" /> <asp:Parameter Name="original_ProductID" Type="Int32" /> </DeleteParameters> <UpdateParameters> <asp:Parameter Name="UnitPrice" Type="Decimal" /> <asp:Parameter Name="Quantity" Type="Int16" /> <asp:Parameter Name="original_OrderID" Type="Int32" /> <asp:Parameter Name="original_ProductID" Type="Int32" /> </UpdateParameters> <SelectParameters> <asp:ControlParameter ControlID="productSelector" Name="ProductID" PropertyName="SelectedValue" Type="Int32" /> </SelectParameters> <InsertParameters> <asp:Parameter Name="OrderID" Type="Int32" /> <asp:Parameter Name="ProductID" Type="Int32" /> <asp:Parameter Name="UnitPrice" Type="Decimal" /> <asp:Parameter Name="Quantity" Type="Int16" /> </InsertParameters> </asp:SqlDataSource>
追加<
の DeleteParameters>、<UpdateParameters>、InsertParameters>、 <DeleteCommand、InsertCommand、UpdateCommand の各プロパティに注意してください。 この例では削除することに関心があるため、InsertCommand、UpdateCommand、UpdateParameters、<InsertParameters><>を安全に削除できます。
DeleteCommand を使用するように SqlDataSource を構成すると、関連付けられている GridView のスマート タグに [削除を有効にする] というチェックボックスが含まれます。 これをチェックすると、CommandField と [削除] ボタンが GridView に追加されます (図 36 を参照)。
図 36 (図をクリックすると大きな画像が表示されます)
そして、それはそれのすべてです! コードは必要ありません。 欠点の 1 つは、 GridView から削除しても確認できない点です。 つまり、特定の GridView 行に対して [削除] ボタンがクリックされるとすぐに、ページはポストバックされ、その行は失われます。 エンド ユーザーには、削除の確認を求めるプロンプトが表示されるのが理想的です。 これを行う方法については、次のセクション「Client-Side スクリプトを使用して削除を確認する」を参照してください。
ASP.NET ページの宣言型構文を次に示します。 このマークアップを調査するときは、次の点に注意してください。
- orderDetailsGridView GridView の DataKeyNames フィールドは、Order Details テーブル (OrderID と ProductID) の主キー フィールドに設定されます。 GridView の [削除] ボタンをクリックすると、ページがポストバックされ、Delete ステートメントの OrderID パラメーター値と ProductID パラメーター値に対してクリックされた GridView 行の DataKeyNames 値を使用して、SqlDataSource の DeleteCommand が実行されます。
<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <h2>You Can Delete Order Detail Information for Orders that Have Included Shipments of the Selected Product </h2> <asp:SqlDataSource ID="productListingDataSource" ConnectionString="<%$ ConnectionStrings:NWConnectionString %>" SelectCommand="SELECT [ProductID], [ProductName] FROM [Products]" Runat="server"> </asp:SqlDataSource> <asp:DropDownList ID="productSelector" Runat="server" DataSourceID="productListingDataSource" DataTextField="ProductName" DataValueField="ProductID" AutoPostBack="True"> </asp:DropDownList> <asp:SqlDataSource ID="orderDetailsForProduct" DataSourceMode="DataReader" ConnectionString="<%$ ConnectionStrings:NWConnectionString %>" SelectCommand="SELECT [OrderID], [ProductID], [UnitPrice], [Quantity] FROM [Order Details] WHERE ([ProductID] = @ProductID2)" Runat="server" DeleteCommand="DELETE FROM [Order Details] WHERE [OrderID] = @original_OrderID AND [ProductID] = @original_ProductID"> <DeleteParameters> <asp:Parameter Type="Int32" Name="OrderID"></asp:Parameter> <asp:Parameter Type="Int32" Name="ProductID"></asp:Parameter> </DeleteParameters> <SelectParameters> <asp:ControlParameter Name="ProductID2" Type="Int32" ControlID="productSelector" PropertyName="SelectedValue"></asp:ControlParameter> </SelectParameters> </asp:SqlDataSource> <asp:GridView ID="orderDetailsGridView" Runat="server" AutoGenerateColumns="False" DataSourceID="orderDetailsForProduct" DataKeyNames="OrderID,ProductID" BorderColor="Tan" CellPadding="2" BackColor="LightGoldenrodYellow" BorderWidth="1px" ForeColor="Black" GridLines="None" OnRowDeleting="orderDetailsGridView_RowDeleting"> <FooterStyle BackColor="Tan"></FooterStyle> <PagerStyle ForeColor="DarkSlateBlue" HorizontalAlign="Center" BackColor="PaleGoldenrod"></PagerStyle> <HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle> <AlternatingRowStyle BackColor="PaleGoldenrod"></AlternatingRowStyle> <Columns> <asp:CommandField DeleteText="Delete Order Line Item" ShowDeleteButton="True"></asp:CommandField> <asp:BoundField HeaderText="Order ID" DataField="OrderID" SortExpression="OrderID"> <ItemStyle HorizontalAlign="Center"></ItemStyle> </asp:BoundField> <asp:BoundField HeaderText="Quantity" DataField="Quantity" SortExpression="Quantity" DataFormatString="{0:d}"> <ItemStyle HorizontalAlign="Right"></ItemStyle> </asp:BoundField> <asp:BoundField HeaderText="Unit Price" DataField="UnitPrice" SortExpression="UnitPrice" DataFormatString="{0:c}"> <ItemStyle HorizontalAlign="Right"></ItemStyle> </asp:BoundField> </Columns> <SelectedRowStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedRowStyle> </asp:GridView> </div> </form> </body> </html>
GridView では、削除を支援するために、基になるデータが実際に削除される前に発生する RowDeleting イベントが提供されます。 一部の条件が満たされた場合にのみデータを削除できるようにする場合は、このイベントのイベント ハンドラーを作成できます。 イベント ハンドラーは、2 番目のパラメーターとして GridViewDeleteEventArgs 型のオブジェクトとして渡されます。このオブジェクトには、いくつかの便利なプロパティがあります。
- 削除される行の値に関するデータを提供する値。
- キャンセル。 True に設定すると、削除がショートサーキットされます。
基になるレコードが正常に削除された後に発生する RowDeleted イベントもあります。
ObjectDataSource からのデータの削除
ObjectDataSource にバインドされている GridView 内のレコードの削除は、基本的に、SqlDataSource にバインドされている GridView からレコードを削除することと同じです。 唯一の違いはバックエンドにあります。 ObjectDataSource がアクセスするデータ アクセス層クラスは、レコードを削除するためのメソッドを提供する必要があります。
ObjectDataSource によって公開されるデータを操作する場合、このデータを操作する方法は 2 つあります。 ObjectDataSource には、次の 2 つの値のいずれかを指定できる ConflictDetection プロパティがあります。
- OverwriteChanges (既定値)、または
- CompareAllValues
OverwriteChanges オプションは、他のユーザーが現在のユーザーによって削除または更新されるデータをステップ実行する可能性を気にしない場合に使用することを目的としています。 このシナリオでは、削除または更新を最後にコミットしたユーザーが勝者になります。 ただし、他のユーザーが基になるデータに変更を加えたと検出した場合に、ユーザーの変更をショートサーキットしたい場合があります。 このようなシナリオでは、 CompareAllValues オプションを 使用します。
ObjectDataSource の ConflictDetection プロパティをメンション理由は、Delete メソッドのシグネチャがその設定によって異なるためです。 Delete メソッドは、削除するレコードを一意に識別するために必要な数のパラメーターを受け入れる必要があります。 たとえば、Products テーブルから製品を削除するメソッドを作成する場合、各製品は ProductID によって一意に識別されるため、少なくともメソッドは ProductID 整数入力を受け入れる必要があります。 OverwriteChanges モードを使用する場合、Delete メソッドは、この最小限の入力セットのみを受け入れる必要があります。 ただし、 CompareAllValues オプションを使用する場合、Delete メソッドシグネチャは主キー フィールドだけでなく、 GridView で定義されている他のフィールドも受け入れる必要があります。
これは、この時点で少し混乱を招く可能性があり、当然のように聞こえます。 しかし、具体的な例では、物事がより明確になります。 Order Details テーブルには複合主キーがあり、1 つは ProductID と OrderID の 2 つのフィールドで構成されます。 GridView には、OrderID、Quantity、UnitPrice の 3 つのフィールドが表示されます。 ここで、 ObjectDataSource のConflictDetection プロパティを OverwriteChanges に設定して削除を実行する場合、Delete メソッドは次のようにする必要があります。
' Visual Basic .NET Public Shared Sub DeleteMethod(ByVal original_OrderID As Integer, _ ByVal original_ProductID As Integer) ... End Sub // C# public static void DeleteMethod(int original_OrderID, int original_ProductID) { ... }
ただし、 ConflictDetection プロパティが OverwriteChanges に設定されている場合、Delete メソッドは次のようになります。
' Visual Basic .NET Public Shared Sub DeleteMethod(ByVal original_OrderID As Integer, _ ByVal original_ProductID As Integer, _ ByVal original_Quantity as Integer, _ ByVal original_UnitPrice as Decimal) ... End Sub // C# public static void DeleteMethod(int original_OrderID, int original_ProductID, int original_Quantity, decimal original_UnitPrice) { ... }
このデモでは 、OverwriteChanges アプローチを 使用しました。 次に、 OrderDetailDAL クラスの Delete メソッドを示します。
DeleteOrderDetail メソッド (Visual Basic)
Public Class OrderDetailDAL ... Public Shared Sub DeleteOrderDetail(ByVal original_OrderID _ As Integer, ByVal original_ProductID As Integer) ' deletes a specified Order Details record ' from the Northwind Products table Dim sql As String = _ "DELETE FROM [Order Details] WHERE OrderID = " & _ "@OrderID AND ProductID = @ProductID" Using myConnection As New _ SqlConnection(ConfigurationManager.ConnectionStrings("NWConnectionString") .ConnectionString) Dim myCommand As New SqlCommand(sql, myConnection) myCommand.Parameters.Add(New SqlParameter("@OrderID", _ original_OrderID)) myCommand.Parameters.Add(New SqlParameter("@ProductID", _ original_ProductID)) myConnection.Open() myCommand.ExecuteNonQuery() myConnection.Close() End Using End Sub End Class The DeleteOrderDetail Method (C#) public class OrderDetailDAL { ... public static void DeleteOrderDetail(int original_OrderID, int original_ProductID) { // deletes a specified Order Details record // from the Northwind Products table string sql = "DELETE FROM [Order Details] WHERE OrderID = " + "@OrderID AND ProductID = @ProductID"; using (SqlConnection myConnection = new SqlConnection( ConfigurationManager.ConnectionStrings["NWConnectionString"].ConnectionString)) { SqlCommand myCommand = new SqlCommand(sql, myConnection); myCommand.Parameters.Add(new SqlParameter("@OrderID", original_OrderID)); myCommand.Parameters.Add(new SqlParameter("@ProductID", original_ProductID)); myConnection.Open(); myCommand.ExecuteNonQuery(); myConnection.Close(); } } }
ObjectDataSource を削除用に構成することは、SqlDataSource とは異なるトリフレです。 使用するクラスを選択した後、[削除] タブを参照し、図 37 に示すようにドロップダウン リストでメソッド名を選択することで、データを削除するメソッドを指定できます。
図 37
ObjectDataSource を削除用に構成すると、GridView を削除用に構成する手順は、SqlDataSource の場合と同じになります。最終的な結果と同じです。 次に、ASP.NET ページの宣言構文を示します。
<%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <div> <h2>You Can Delete Order Detail Information for Orders that Have Included Shipments of the Selected Product </h2> <asp:ObjectDataSource ID="productListingDataSource" Runat="server" TypeName="ProductDAL" SelectMethod="GetProducts"> </asp:ObjectDataSource> <asp:DropDownList ID="productSelector" Runat="server" DataSourceID="productListingDataSource" DataTextField="ProductName" DataValueField="ProductID" AutoPostBack="True"> </asp:DropDownList> <asp:ObjectDataSource ID="orderDetailsForProduct" Runat="server" SelectMethod="GetOrderDetailsByProductID" TypeName="OrderDetailDAL" DeleteMethod="DeleteOrderDetail"> <DeleteParameters> <asp:Parameter Type="Int32" Name="original_OrderID"></asp:Parameter> <asp:Parameter Type="Int32" Name="original_ProductID"></asp:Parameter> </DeleteParameters> <SelectParameters> <asp:ControlParameter Name="productID" Type="Int32" ControlID="productSelector" PropertyName="SelectedValue"></asp:ControlParameter> </SelectParameters> </asp:ObjectDataSource> <asp:GridView ID="orderDetailsGridView" Runat="server" AutoGenerateColumns="False" DataSourceID="orderDetailsForProduct" DataKeyNames="OrderID,ProductID" BorderColor="Tan" CellPadding="2" BackColor="LightGoldenrodYellow" BorderWidth="1px" ForeColor="Black" GridLines="None"> <FooterStyle BackColor="Tan"></FooterStyle> <PagerStyle ForeColor="DarkSlateBlue" HorizontalAlign="Center" BackColor="PaleGoldenrod"></PagerStyle> <HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle> <AlternatingRowStyle BackColor="PaleGoldenrod"></AlternatingRowStyle> <Columns> <asp:CommandField DeleteText="Delete Order Line Item" ShowDeleteButton="True"></asp:CommandField> <asp:BoundField HeaderText="Order ID" DataField="OrderID" SortExpression="OrderID"> <ItemStyle HorizontalAlign="Center"></ItemStyle> </asp:BoundField> <asp:BoundField HeaderText="Quantity" DataField="Quantity" SortExpression="Quantity" DataFormatString="{0:d}"> <ItemStyle HorizontalAlign="Right"></ItemStyle> </asp:BoundField> <asp:BoundField HeaderText="Unit Price" DataField="UnitPrice" SortExpression="UnitPrice" DataFormatString="{0:c}"> <ItemStyle HorizontalAlign="Right"></ItemStyle> </asp:BoundField> </Columns> <SelectedRowStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedRowStyle> </asp:GridView> </div> </div> </form> </body> </html>
Client-Side スクリプトを使用して削除を確認する
先ほど確認した 2 つの削除の例では、削除時に確認が提供されません。 つまり、エンド ユーザーが [削除] ボタンをクリックするとすぐにポストバックが発生し、レコードが削除されました。 誤って削除されないようにするには、エンド ユーザーに何らかの確認を提供し、レコードを削除する前に削除することを確認することをお勧めします。 一般的な確認方法の 1 つは、クライアント側の確認メッセージ ボックスを使用することです。 確認メッセージ ボックスは、[OK] ボタンと [キャンセル] ボタンと共に表示され、ユーザーが [キャンセル] をクリックするとフォームの送信がショートします。
ASP.NET 2.0 Button、 LinkButton、 ImageButton Web コントロールには、すべて OnClientClick プロパティが含まれています。このプロパティには、Button をクリックしたときに実行するクライアント側の JavaScript を指定できます。 次のような JavaScript を使用する場合:
return confirm(msg);
ボタンをクリックすると、ユーザーに確認メッセージ ボックスが表示されます ( msg は、確認メッセージ ボックスに表示する文字列です)。 ユーザーが確認メッセージ ボックスの [キャンセル] ボタンをクリックすると、フォームはポストバックされません。[OK] ボタンをクリックすると、フォームが送信されます。
残念ながら、 GridView の CommandField には OnClientClick プロパティは含まれません。 ただし、CommandName が Delete の GridView 内のボタンをクリックすると、GridView は関連付けられたレコードを削除します。 したがって、CommandName が Delete の Button (または LinkButton または ImageButton) を含む TemplateField を追加することで、独自の [削除] ボタンを作成できます。 この追加された Button は、適切な OnClientClick プロパティ値を持つことができます。 [削除] ボタンがクリックされたときに確認メッセージ ボックスを表示するように GridView を変更する方法を次に示します。
<asp:GridView ID="orderDetailsGridView" Runat="server" DataSourceID="orderDetailsForProduct" AutoGenerateColumns="False" DataKeyNames="OrderID,ProductID" BorderColor="Tan" CellPadding="2" BackColor="LightGoldenrodYellow" BorderWidth="1px" ForeColor="Black" GridLines="None"> <FooterStyle BackColor="Tan"></FooterStyle> <PagerStyle ForeColor="DarkSlateBlue" HorizontalAlign="Center" BackColor="PaleGoldenrod"></PagerStyle> <HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle> <AlternatingRowStyle BackColor="PaleGoldenrod"></AlternatingRowStyle> <Columns> <asp:TemplateField><ItemTemplate> <asp:LinkButton ID="LinkButton1" Runat="server" OnClientClick="return confirm('Are you sure you want to delete this record?');" CommandName="Delete">Delete Order Line Item</asp:LinkButton> </ItemTemplate> </asp:TemplateField> <asp:BoundField HeaderText="Order ID" DataField="OrderID" SortExpression="OrderID"> <ItemStyle HorizontalAlign="Center"></ItemStyle> </asp:BoundField> <asp:BoundField HeaderText="Quantity" DataField="Quantity" SortExpression="Quantity" DataFormatString="{0:d}"> <ItemStyle HorizontalAlign="Right"></ItemStyle> </asp:BoundField> <asp:BoundField HeaderText="Unit Price" DataField="UnitPrice" SortExpression="UnitPrice" DataFormatString="{0:c}"> <ItemStyle HorizontalAlign="Right"></ItemStyle> </asp:BoundField> </Columns> <SelectedRowStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedRowStyle> </asp:GridView>
TemplateField には、CommandName プロパティが Delete に設定され、OnClientClick プロパティが confirm('このレコードを削除してもよろしいですか?') を返すように設定された LinkButton があることに注意してください。 図 38 に示すように、[削除] ボタンをクリックすると、確認メッセージ ボックスが表示され、メッセージ ボックスの [OK] ボタンがクリックされた場合にのみレコードが削除されます。
図 38