DataGridに行の削除機能を追加 (ch57DataGrid1.aspx)

 

ここで作成するサンプルは、DataGridOracleデータベースのCustomers表をバインドして表示します

 

Customers表から行(レコード)を抽出するには、パッケージ(CustomerPackage)に登録されているストアドプロシージャ(GetCustomersGT40)を使用します。Customers表から行を削除するには、ストアドプロシージャ(DeleteCustomers)を使用します。

 

iSQL*PlusまたはSQL*Plusを起動して、事前にパッケージ仕様部(C:\vbora\sql\CustomerPackage.sql)とパッケージ本体部(C:\vbora\sql\CustomerPackageBody.sql)を作成してください。

 

パッケージ仕様部(CustomerPackage.sql)

CREATE OR REPLACE PACKAGE CustomerPackage AS

  TYPE rcurCustomers IS REF CURSOR;

  PROCEDURE GetCustomersGT40(

    orcurCustomers OUT rcurCustomers);

  PROCEDURE DeleteCustomers(

    iCustomerID IN NUMBER);

END CustomerPackage;

 

パッケージ本体部(CustomerPackageBody.sql)

CREATE OR REPLACE PACKAGE BODY CustomerPackage AS

  PROCEDURE GetCustomersGT40(

    orcurCustomers OUT rcurCustomers) IS

  BEGIN

    OPEN orcurCustomers FOR

      SELECT *

FROM Customers

      WHERE CustomerID > 40

      ORDER BY CustomerID;

  END GetCustomersGT40;

  PROCEDURE DeleteCustomers(

    iCustomerID IN NUMBER) IS

  BEGIN

    DELETE

    FROM Customers

    WHERE CustomerID = iCustomerID;

  END DeleteCustomers;

END CustomerPackage;

 

このサンプルでは、以下のノウハウを習得することができます。

 

DataGridに行(レコード)を削除する機能を追加する方法

DataGridに削除ボタン列を追加する方法

▼削除ボタンクリック時確認メッセージを表示する方法

DataGridDeleteCommandイベントの使い方

▼ストアドプロシージャから削除してレコード件数を取得する方法

▼削除用のストアドプロシージャに楽観的ロックを組み込む方法

▼暗黙カーソル(SQL%ROWCOUNT)の使い方

 

1. Webフォーム追加

 

ソリューションエクスプローラからフォルダ[ch5]を右クリックして、新規Webフォーム「ch57DataGrid1」を追加します。

 

2. DataGridを作成して編集機能追加

 

DataGridに編集機能を追加します。

 

fig5-7-1

DataGridに編集機能追加

 

3. 削除ボタン列作成

 

DataGrid1の右クリックから[プロパティビルダ]を選択します。「DataGrid1プロパティ」が表示されたら、左側の[]を選択します。「使用可能な列」から[ボタン列]をクリックして展開します。[削除]を選択したらiconRightArrowをクリックします。「選択された列」に削除ボタン列が表示されます。ボタン列(ButtonColumn)プロパティの「ボタンの種類」から[PushButton]を選択します。

 

fig5-7-2

プロパティビルダの[]から削除ボタン列作成

 

4. 削除ボタンを中央揃え

 

「プロパティビルダ」の左側から[書式]を選択します。画面中央の「オブジェクト」から[]をクリックして展開します。[Columns[5]]をクリックして展開したら[項目]を選択します。「水平方向の配置」から[中央]を選択して削除ボタンを中央揃えに設定します。

 

fig5-7-3

プロパティビルダの[書式]からさ削除ボタンを中央揃えに設定

 

5. DeleteCommandイベント作成

 

メニューバーから[表示][コード]を選択してコードビューに切り替えます。コードビュー左上の「クラス名」のドロップダウンリストから[DataGrid1]を選択します。右上の「メソッド名」のドロップダウンリストから[DeleteCommand]を選択します。DataGrid1_DeleteCommandイベントが作成されたら、以下のコードを追加します。

 

Private Sub DataGrid1_DeleteCommand(ByVal source As Object,

cccByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)

cccHandles DataGrid1.DeleteCommand

  Dim intCustomerID As Integer = DataGrid1.DataKeys(e.Item.ItemIndex)

 

  DeleteRecord(intCustomerID)

  BindGrid()

End Sub

 

DataGrid1_DeleteCommandイベントの後に、Function DeleteRecordを追加します。

 

Private Function DeleteRecord(ByVal intCustomerID As Integer) As String

  Dim con As New OracleConnection(ConfigurationSettings.AppSettings("conStringOraNw"))

  Dim cmd As New OracleCommand("CustomerPackage.DeleteCustomers", con)

 

  cmd.CommandType = CommandType.StoredProcedure

  cmd.BindByName = True

  cmd.Parameters.Add("iCustomerID", OracleDbType.Int32).Value = intCustomerID

  con.Open()

  Dim intRetValue As Integer = cmd.ExecuteNonQuery()

  con.Close()

  Return intRetValue

End Function

 

6. ItemDataBoundイベント作成

 

コードビュー左上の「クラス名」のドロップダウンリストから[DataGrid1]を選択します。右上の「メソッド名」のドロップダウンリストから[ItemDataBound]を選択します。DataGrid1_ItemDataBoundイベントが作成されたら、以下のコードを追加します。

 

Private Sub DataGrid1_ItemDataBound(ByVal sender As Object,

cccByVal e As System.Web.UI.WebControls.DataGridItemEventArgs)

cccHandles DataGrid1.ItemDataBound

  If e.Item.ItemType = ListItemType.Item OrElse _

    e.Item.ItemType = ListItemType.AlternatingItem Then

    Dim btn As Button = CType(e.Item.Cells(5).Controls(0), Button)

    btn.Attributes.Add("onclick", "return confirm('削除してよろしいですか?')")

  End If

End Sub

 

7. ブラウザに表示

 

ソリューションエクスプローラから[ch57DataGrid1.aspx]を右クリックしてブラウザに表示します。DataGridCustomers表が表示されます。DataGridの右端から[削除]をクリックすると「削除してよろしいですか?」の確認メッセージが表示されます。削除するときは[OK]、中止するときは[キャンセル]をクリックします。

 

 

fig5-7-4

DataGridから[削除]をクリックした例

 

■解説

 

DataGridに削除機能を組み込むには、「プロパティビルダ」の[]から[削除]を追加します。削除ボタン列のプロパティ「テキスト」には、ボタンの表題を入力します。デフォルトで「削除」が表示されます。「コマンド名」には、削除を意味する「Delete」を入力します。「ボタンの種類」からは、[LinkButton]または[PushButton]のいずれかを選択します。デフォルトは、[LinkButton]になります。「プロパティビルダ」から[OK]をクリックすると、削除ボタン列が生成されます。

 

<asp:DataGrid id="DataGrid1" runat="server"

  AutoGenerateColumns="False">

  <Columns>

    <asp:BoundColumn DataField="CustomerID"

      ReadOnly="True" HeaderText="ID">

    </asp:BoundColumn>

    <asp:BoundColumn DataField="CompanyName"

      HeaderText="得意先">

    </asp:BoundColumn>

    <asp:BoundColumn DataField="ContactName"

      HeaderText="担当">

    </asp:BoundColumn>

    <asp:BoundColumn DataField="Phone"

      HeaderText="電話">

    </asp:BoundColumn>

    ・・・

    <asp:ButtonColumn Text="削除"

      ButtonType="PushButton" CommandName="Delete">

    </asp:ButtonColumn>

  </Columns>

</asp:DataGrid>

 

DataGridから[削除]をクリックするとWebページがポストバックされて、Page_LoadDataGrid1_DeleteCommandの順にイベントが発生します。

 

Page_Loadイベントでは、IsPostBackプロパティを調べて初期ロードのときBindGridメソッドを実行します。

 

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)

cccHandles MyBase.Load

  If Not IsPostBack Then

    BindGrid()

  End If

End Sub

 

BindGridメソッドは、CreateDataSetメソッドを実行してCustomers表のDataSetを作成します。CreateDataSetメソッドの引数には、パッケージ(CustomerPackage)のストアドプロシージャ(GetCustomersGT40[1])を指定します。DataGridDataSourceプロパティにDataSetのオブジェクトを設定して、DataBindメソッドを実行すると、DataGridDataSetがバインドされてCustomers表が表示されます。

 

Private Sub BindGrid()

  With DataGrid1

    .DataSource = CreateDataSet("CustomerPackage.GetCustomersGT40")

    .DataKeyField = "CustomerID"

    .DataBind()

  End With

End Sub

 

DataGrid1_DeleteCommandイベントでは、DataGridCommandEventArgsオブジェクトのItem.ItemIndexプロパティからカレントのアイテム番号を取得します。DataGridDataKeysコレクションからカレント行の主キー(CustomerID)を取得して変数に保存します。DeleteRecordメソッドを実行してカレント行をOracleデータベースから削除します。DeleteRecordメソッドの引数には、主キーを指定します。BindGridメソッドを実行して削除した行をDataGridから消去します。

 

Private Sub DataGrid1_DeleteCommand(ByVal source As Object,

cccByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)

cccHandles DataGrid1.DeleteCommand

  Dim intCustomerID As Integer = DataGrid1.DataKeys(e.Item.ItemIndex)

 

  DeleteRecord(intCustomerID)

  BindGrid()

End Sub

 

DeleteRecordメソッドは、パッケージ(CustomerPackage)のストアドプロシージャ(DeleteCustomers)を使用してCustomers表から行を削除します。OracleConnectionOracleCommandのインスタンスを生成したら、OracleCommandオブジェクトのCommandTypeプロパティにCommandType.StoredProcedureを設定します。さらに、BindByNameプロパティにTrueを設定して名前指定のパラメータを使用することを指示します。

 

ParametersコレクションのAddメソッドでパラメータを追加するとき、引数にはストアドプロシージャに宣言しているパラメータ名を指定します。

 

PROCEDURE DeleteCustomers(

  iCustomerID IN NUMBER) IS

BEGIN

  DELETE

  FROM Customers

  WHERE CustomerID = iCustomerID;

END DeleteCustomers;

 

cmd.Parameters.Add("iCustomerID", OracleDbType.Int32).Value = intCustomerID

 

OracleConnectionオブジェクトのOpenメソッドでOracleデータベースを開いたら、OracleCommandオブジェクトのExecuteNonQueryメソッドでストアドプロシージャのDELETE文を実行します。次に、OracleConnectionオブジェクトのCloseメソッドでOracleデータベースを閉じて戻ります。このメソッドからは、戻り値として削除した行数(レコード数[2])を返します。

 

Private Function DeleteRecord(ByVal intCustomerID As Integer) As String

  Dim con As New OracleConnection(ConfigurationSettings.AppSettings("conStringOraNw"))

  Dim cmd As New OracleCommand("CustomerPackage.DeleteCustomers", con)

 

  cmd.CommandType = CommandType.StoredProcedure

  cmd.BindByName = True

  cmd.Parameters.Add("iCustomerID", OracleDbType.Int32).Value = intCustomerID

  con.Open()

  Dim intRetValue As Integer = cmd.ExecuteNonQuery()

  con.Close()

  Return intRetValue

End Function

 

 

STEP UP

楽観的ロックを組み込む (ch57DataGrid1a.aspx)

 

ch57DataGrid1.aspxのサンプルは、排他制御を一切考慮していませんので、他のユーザーが更新したデータを削除する可能性があります。ここで紹介するサンプルは、楽観的ロックを採用してすでに他のユーザーがレコードを変更しているときは、排他制御エラーにして再試行させるようにします。以下に、ch57DataGrid1.aspxのサンプルに楽観的ロックを組み込む手順を解説します。

 

1. ストアドプロシージャの追加

 

パッケージの仕様部(CustomerPackage)と本体部(CustomerPackageBody)に、ストアドプロシージャ(DeleteCustomersConcurrencyIDRc)を追加します。このストアドプロシージャは、得意先ID(CustomerID)と更新回数(ConcurrencyID)が一致したときに行(レコード)を削除するように改善しています。また、出力パラメータ(oRowCount)に削除されたレコード数を設定して返します。SQL%ROWCOUNTには、削除されたレコード数が格納されています。これで、レコードが他のユーザーから変更されていないときのみ削除されます。

 

パッケージの仕様部

CREATE OR REPLACE PACKAGE CustomerPackage AS

  PROCEDURE DeleteCustomersConcurrencyIDRc(

    iCustomerID IN NUMBER,

    iConcurrencyID IN NUMBER,

    oRowCount OUT NUMBER);

END CustomerPackage;

 

パッケージの本体部

CREATE OR REPLACE PACKAGE BODY CustomerPackage AS

  PROCEDURE DeleteCustomersConcurrencyIDRc(

    iCustomerID IN NUMBER,

    iConcurrencyID IN NUMBER,

    oRowCount OUT NUMBER) IS

  BEGIN

    DELETE

    FROM Customers

    WHERE CustomerID = iCustomerID

      AND ConcurrencyID = iConcurrencyID;

    oRowCount := SQL%ROWCOUNT;

  END DeleteCustomersConcurrencyIDRc;

END CustomerPackage;

 

 

2. DataGridに連結列追加

 

DataGrid1の「プロパティビルダ」を表示したら、[]から連結列を作成して連結列(BoundColumn)プロパティの「データフィールド」に「ConcurrencyID」を入力します。[読み取り専用]をクリックしてチェックマークを付けます。「可視」をクリックしてチェックを外します。これで、ConcurrencyIDの連結列が不可視になります。ここで追加した「ConcurrencyID」の連結列を上下の矢印キーを使用して「電話」の連結列の次に移動します。

 

 

3. DeleteCommandイベントを書き換え

 

コードビューに切り替えたら、DataGrid1_DeleteCommandを以下のように書き換えます。ConcurrencyIDの連結列から更新回数を取得して変数に保存します。DeleteRecordメソッドに引数intConcurrencyIDを追加します。戻り値が「0」のときは、排他制御エラーのメッセージを表示して再試行させます。

 

Private Sub DataGrid1_DeleteCommand(ByVal source As Object,

cccByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)

cccHandles DataGrid1.DeleteCommand

  Dim intConcurrencyID As Integer = Int32.Parse(e.Item.Cells(4).Text)

  Dim intCustomerID As Integer = DataGrid1.DataKeys(e.Item.ItemIndex)

 

  Dim intAffectedRecords As Integer = DeleteRecord(intCustomerID, intConcurrencyID)

  If intAffectedRecords = 0 Then

    Response.Write("該当行が他のユーザーから更新されています!<br>再試行してください.<br>")

  End If

  BindGrid()

End Sub

 

4. Function DeleteRecordの書き換え

 

Function DeleteRecordを以下のように書き換えます。入力パラメータ(iConcurrencyID)と出力パラメータ(oRowCount)を追加します。ExecuteNonQuery[3]メソッド実行後、出力パラメータから削除されたレコード数を取得します。

 

Private Function DeleteRecord(ByVal intCustomerID As Integer, _

  ByVal intConcurrencyID As Integer) As String

  Dim con As New OracleConnection(ConfigurationSettings.AppSettings("conStringOraNw"))

  Dim cmd As New OracleCommand("CustomerPackage.DeleteCustomersConcurrencyIDRc", con)

 

  cmd.CommandType = CommandType.StoredProcedure

  cmd.BindByName = True

  cmd.Parameters.Add("iCustomerID", OracleDbType.Int32).Value = intCustomerID

  cmd.Parameters.Add("iConcurrencyID", OracleDbType.Int32).Value = intConcurrencyID

  cmd.Parameters.Add("oRowCount", OracleDbType.Int32).Direction = ParameterDirection.Output

  con.Open()

  cmd.ExecuteNonQuery()

  Dim intRetValue As Integer = Int32.Parse(cmd.Parameters("oRowCount").Value)

  con.Close()

  Return intRetValue

End Function

 

5. ItemDataBoundイベントを書き換え

 

DataGrid1_ItemDataBoundイベントを以下のように書き換えます。DataGridConcurrencyIDの連結列を追加したので、削除ボタンのオブジェクトを取得するコードをCType(e.Item.Cells(5).Controls(0), Button)からCType(e.Item.Cells(6).Controls(0), Button)に書き換えます。

 

Private Sub DataGrid1_ItemDataBound(ByVal sender As Object,

cccByVal e As System.Web.UI.WebControls.DataGridItemEventArgs)

cccHandles DataGrid1.ItemDataBound

  If e.Item.ItemType = ListItemType.Item OrElse _

    e.Item.ItemType = ListItemType.AlternatingItem Then

    Dim btn As Button = CType(e.Item.Cells(6).Controls(0), Button)

    btn.Attributes.Add("onclick", "return confirm('削除してよろしいですか?')")

  End If

End Sub

 

 



[1] このストアドプロシージャは、Customers表から得意先ID40以上の行(レコード)を抽出します。

[2] Oracleデータベースに対してExecuteNonQueryメソッドを実行したときは、常に「-1」が返ります。レコードが正常に削除されたか調べるには、出力パラメータに削除されたレコード数(%ROWCOUNT)を返します。

[3] AccessMySQLSQL Serverに対してExecuteNonQueryを実行すると更新されたレコード数が返されますが、Oracleに対して実行すると常に「-1」が返されます。