一括更新 (C#)

作成者 Scott Mitchell

PDF をダウンロードする

1 回の操作で複数のデータベース レコードを更新する方法について説明します。 ユーザー インターフェイス レイヤーでは、各行が編集可能な GridView を構築します。 データ アクセス層では、トランザクション内で複数の更新操作をラップして、すべての更新が成功するか、すべての更新がロールバックされるようにします。

イントロダクション

前の チュートリアル では、データ アクセス層を拡張してデータベース トランザクションのサポートを追加する方法について説明しました。 データベース トランザクションでは、一連のデータ変更ステートメントが 1 つのアトミック操作として扱われることが保証されます。これにより、すべての変更が失敗するか、すべて成功します。 低レベルの DAL 機能が片付いたので、私たちはバッチデータ変更インターフェースの作成に注意を向ける準備ができました。

このチュートリアルでは、各行が編集可能な GridView を作成します (図 1 を参照)。 各行は編集インターフェイスでレンダリングされるため、[編集]、[更新]、[キャンセル] ボタンの列は必要ありません。 代わりに、ページ上に 2 つの [Update Products]\(製品の更新\) ボタンがあります。このボタンをクリックすると、GridView 行が列挙され、データベースが更新されます。

GridView の各行は編集可能です

図 1: GridView の各行は編集可能です (フルサイズの画像を表示する をクリックします)

では、始めましょう。

注

バッチ更新の実行チュートリアルでは、DataList コントロールを使用してバッチ編集インターフェイスを作成しました。 このチュートリアルは、GridView を使用する前のチュートリアルとは異なり、バッチ更新はトランザクションのスコープ内で実行されます。 このチュートリアルを完了したら、前のチュートリアルに戻り、前のチュートリアルで追加したデータベース トランザクション関連の機能を使用するように更新することをお勧めします。

すべての GridView 行を編集可能にする手順を調べる

「データの挿入、更新、および削除の概要」チュートリアルで説明したように、GridView には、基になるデータを行ごとに編集するための組み込みのサポートが用意されています。 内部的には、GridView は、 EditIndex プロパティを使用して編集可能な行をメモします。 GridView はデータ ソースにバインドされるときに、各行をチェックして、行のインデックスが EditIndex の値と等しいかどうかを確認します。 その場合、その行のフィールドは編集インターフェイスを使用してレンダリングされます。 BoundFields の場合、編集インターフェイスは TextBox であり、 Text プロパティには BoundField の DataField プロパティで指定されたデータ フィールドの値が割り当てられます。 TemplateFields の場合、 EditItemTemplate は ItemTemplateの代わりに使用されます。

ユーザーが行の [編集] ボタンをクリックすると、編集ワークフローが開始されることを思い出してください。 これによりポストバックが発生し、GridView の EditIndex プロパティがクリックされた行のインデックスに設定され、データがグリッドに再バインドされます。 行の [キャンセル] ボタンをクリックすると、ポストバック時に、データをグリッドに再バインドする前に、 EditIndex が -1 の値に設定されます。 GridView の行は 0 からインデックス作成を開始するため、 EditIndex を -1 に設定すると、GridView が読み取り専用モードで表示されます。

EditIndex プロパティは行ごとの編集には適していますが、バッチ編集用には設計されていません。 GridView 全体を編集可能にするには、編集インターフェイスを使用して各行をレンダリングする必要があります。 これを実現する最も簡単な方法は、編集可能な各フィールドが、 ItemTemplateで定義された編集インターフェイスを使用して TemplateField として実装される場所を作成することです。

次のいくつかの手順で、完全に編集可能な GridView を作成します。 手順 1 では、まず GridView とその ObjectDataSource を作成し、BoundFields と CheckBoxField を TemplateFields に変換します。 手順 2 と 3 では、編集インターフェイスを TemplateFields EditItemTemplate から ItemTemplate に移動します。

手順 1: 製品情報の表示

行が編集可能な GridView の作成について心配する前に、まず製品情報を表示します。 BatchUpdate.aspx フォルダーのBatchData ページを開き、ツールボックスからデザイナーに GridView をドラッグします。 GridView の ID を ProductsGrid に設定し、スマート タグから、 ProductsDataSourceという名前の新しい ObjectDataSource にバインドすることを選択します。 ProductsBLL クラスの GetProducts メソッドからデータを取得するように ObjectDataSource を構成します。

ProductsBLL クラスを使用するように ObjectDataSource を構成する

図 2: ProductsBLL クラスを使用するように ObjectDataSource を構成する (フルサイズの画像を表示する をクリックします)。

GetProducts メソッドを使用して製品データを取得する

図 3: GetProducts メソッドを使用して製品データを取得する (フルサイズの画像を表示する をクリックします)。

GridView と同様に、ObjectDataSource の変更機能は行ごとに機能するように設計されています。 レコードのセットを更新するには、データをバッチ処理して BLL に渡すコードを ASP.NET ページの分離コード クラスに少し記述する必要があります。 そのため、ObjectDataSource の [UPDATE]、[INSERT]、[DELETE] タブのドロップダウン リストを [なし] に設定します。 [完了] をクリックして、ウィザードを完了します。

UPDATE タブ、INSERT タブ、DELETE タブの Drop-Down リストを (なし) に設定する

図 4: [UPDATE]、[INSERT]、[DELETE] タブの [Drop-Down リスト] を [なし] に設定する (フルサイズの画像を表示する 場合はクリックします)

データ ソースの構成ウィザードを完了すると、ObjectDataSource の宣言型マークアップは次のようになります。

<asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>

また、データ ソースの構成ウィザードを完了すると、Visual Studio で GridView の製品データ フィールドの BoundFields と CheckBoxField が作成されます。 このチュートリアルでは、製品の名前、カテゴリ、価格、および廃止された状態の表示と編集のみをユーザーに許可します。 ProductName、CategoryName、UnitPrice、Discontinuedの各フィールドを除くすべてのフィールドを削除し、最初の 3 つのフィールドのHeaderTextプロパティの名前をそれぞれ Product、Category、Price に変更します。 最後に、GridView のスマート タグの [ページングの有効化] チェック ボックスと [並べ替えを有効にする] チェック ボックスをオンにします。

この時点で、GridView には 3 つの BoundField (ProductName、 CategoryName、 UnitPrice) と CheckBoxField (Discontinued) があります。 これら 4 つのフィールドを TemplateFields に変換し、編集インターフェイスを TemplateField の EditItemTemplate からその ItemTemplateに移動する必要があります。

注

データ変更インターフェイスのカスタマイズチュートリアルで TemplateFields の作成とカスタマイズについて説明しました。 BoundFields と CheckBoxField を TemplateFields に変換し、それらの ItemTemplate で編集インターフェイスを定義する各ステップをガイドします。もし途中で行き詰まったり、復習が必要になった場合は、以前のチュートリアルをぜひ参照してください。

GridView のスマート タグで、[列の編集] リンクをクリックして [フィールド] ダイアログ ボックスを開きます。 次に、各フィールドを選択し、[このフィールドを TemplateField に変換] リンクをクリックします。

既存の BoundFields と CheckBoxField を TemplateField に変換する

図 5: 既存の BoundFields と CheckBoxField を TemplateField に変換する

各フィールドが TemplateField になったので、編集インターフェイスを EditItemTemplate から ItemTemplate に移動する準備ができました。

手順 2: ProductName、UnitPrice、およびDiscontinuedのインターフェイスを作成し編集する

ProductName、UnitPrice、およびDiscontinued編集インターフェイスの作成は、この手順のトピックであり、各インターフェイスは TemplateField のEditItemTemplateで既に定義されているため、非常に簡単です。 CategoryName編集インターフェイスの作成は、該当するカテゴリの DropDownList を作成する必要があるため、もう少し複雑です。 この CategoryName 編集インターフェイスは、手順 3 で取り組みます。

ProductName TemplateField から始めましょう。 GridView のスマート タグから [テンプレートの編集] リンクをクリックし、 ProductName TemplateField の EditItemTemplateにドリルダウンします。 テキスト ボックスを選択してクリップボードにコピーし、 ProductName TemplateField の ItemTemplateに貼り付けます。 TextBox の ID プロパティを ProductNameに変更します。

次に、RequiredFieldValidator を ItemTemplate に追加して、ユーザーが各製品名の値を確実に指定できるようにします。 ControlToValidate プロパティを ProductName に設定し、ErrorMessage プロパティを [製品の名前を指定する必要があります] に設定します。 *に Text プロパティを指定します。 ItemTemplateにこれらの追加を行った後、画面は図 6 のようになります。

ProductName TemplateField に TextBox と RequiredFieldValidator が含まれるようになりました

図 6: ProductName TemplateField には、テキスト ボックスと RequiredFieldValidator が含まれています (フルサイズの画像を表示する をクリックします)。

UnitPrice編集インターフェイスの場合は、まずテキスト ボックスをEditItemTemplateからItemTemplateにコピーします。 次に、TextBox の前に $ を配置し、 ID プロパティを UnitPrice に設定し、 Columns プロパティを 8 に設定します。

また、 UnitPrice の ItemTemplate に CompareValidator を追加して、ユーザーが入力した値が $0.00 以上の有効な通貨値であることを確認します。 検証コントロールの ControlToValidate プロパティを UnitPrice に設定し、その ErrorMessage プロパティには「有効な通貨値を入力してください」というメッセージを設定します。 通貨記号は省略してください。それぞれのプロパティについて、Textプロパティを*に、TypeプロパティをCurrencyに、OperatorプロパティをGreaterThanEqualに、そしてValueToCompareプロパティを0に設定してください。

CompareValidator を追加して、入力された価格が負以外の通貨値であることを確認する

図 7: CompareValidator を追加して、入力された価格が負以外の通貨値であることを確認する (フルサイズの画像を表示する をクリックします)

Discontinued TemplateField では、ItemTemplateで既に定義されている CheckBox を使用できます。 IDを Discontinued に設定し、Enabled プロパティを true に設定するだけです。

手順 3: 編集インターフェイスCategoryNameを作成する

CategoryName TemplateFields EditItemTemplateの編集インターフェイスには、CategoryName データ フィールドの値を表示する TextBox が含まれています。 これを、考えられるカテゴリを一覧表示する DropDownList に置き換える必要があります。

注

データ変更インターフェイスのカスタマイズに関するチュートリアルでは、TextBox ではなく DropDownList を含むようにテンプレートをカスタマイズする方法について詳しく説明します。 ここでの手順は完了していますが、簡潔に示されています。 カテゴリ DropDownList の作成と構成の詳細については、「 データ変更インターフェイスのカスタマイズ 」チュートリアルを参照してください。

ツールボックスから CategoryName TemplateField の ItemTemplateに DropDownList をドラッグし、その ID を Categoriesに設定します。 この時点では、通常、スマート タグを使用して DropDownLists のデータ ソースを定義し、新しい ObjectDataSource を作成します。 ただし、これにより、 ItemTemplate内に ObjectDataSource が追加され、その結果、GridView 行ごとに ObjectDataSource インスタンスが作成されます。 代わりに、GridView の TemplateFields の外部に ObjectDataSource を作成しましょう。 テンプレートの編集を終了し、ツールボックスから ProductsDataSource ObjectDataSource の下にあるデザイナーに ObjectDataSource をドラッグします。 新しい ObjectDataSource CategoriesDataSource に名前を付け、 CategoriesBLL クラスの GetCategories メソッドを使用するように構成します。

CategoriesBLL クラスを使用するように ObjectDataSource を構成する

図 8: CategoriesBLL クラスを使用するように ObjectDataSource を構成する (フルサイズの画像を表示する をクリックします)

GetCategories メソッドを使用してカテゴリ データを取得する

図 9: GetCategories メソッドを使用してカテゴリ データを取得します (フルサイズの画像を表示する をクリックします)。

この ObjectDataSource は単にデータを取得するために使用されるため、[UPDATE] タブと [DELETE] タブのドロップダウン リストを [なし] に設定します。 [完了] をクリックして、ウィザードを完了します。

UPDATE タブと DELETE タブの Drop-Down リストを (なし) に設定する

図 10: [更新] タブと [削除] タブの Drop-Down リストを [なし] に設定します (フルサイズの画像を表示する 場合はクリックします)

ウィザードを完了すると、 CategoriesDataSource の宣言型マークアップは次のようになります。

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

CategoriesDataSourceを作成して構成したら、CategoryName TemplateField のItemTemplateに戻り、DropDownList のスマート タグから [データ ソースの選択] リンクをクリックします。 データ ソース構成ウィザードで、最初のドロップダウン リストから CategoriesDataSource オプションを選択し、 CategoryName 表示に使用し、値として CategoryID することを選択します。

DropDownList を CategoriesDataSource にバインドする

図 11: DropDownList を CategoriesDataSource にバインドする (フルサイズの画像を表示する をクリックします)

この時点で、 Categories DropDownList にはすべてのカテゴリが一覧表示されますが、GridView 行にバインドされている製品の適切なカテゴリはまだ自動的には選択されません。 これを実現するには、 Categories DropDownList の SelectedValue を製品の CategoryID 値に設定する必要があります。 DropDownList のスマート タグから [DataBindings の編集] リンクをクリックし、図 12 に示すように、 SelectedValue プロパティを CategoryID データ フィールドに関連付けます。

Product s CategoryID 値を DropDownList s SelectedValue プロパティにバインドする

図 12: 製品の CategoryID 値を DropDownList の SelectedValue プロパティにバインドする

最後の問題の 1 つが残っています。製品に CategoryID 値が指定されていない場合、 SelectedValue の databinding ステートメントによって例外が発生します。 これは、DropDownList にはカテゴリのアイテムのみが含まれており、NULLのCategoryID データベース値を持つ製品にはオプションが提供されないためです。 これを解決するには、DropDownList の AppendDataBoundItems プロパティを true に設定し、新しい項目を DropDownList に追加します。宣言型構文から Value プロパティを省略します。 つまり、 Categories DropDownList の宣言構文が次のようになっていることを確認します。

<asp:DropDownList ID="Categories" runat="server" AppendDataBoundItems="True" 
    DataSourceID="CategoriesDataSource" DataTextField="CategoryName" 
    DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'>
    <asp:ListItem Value=">-- Select One --</asp:ListItem>
</asp:DropDownList>

<asp:ListItem Value=""> (Select One) のValue属性が空の文字列に明示的に設定されていることに注意してください。 ケースを処理するためにこの追加の DropDownList 項目が必要な理由と、空の文字列への NULL プロパティの割り当てが不可欠な理由については、「Value」チュートリアルを参照してください。

注

ここには、言及する価値がある潜在的なパフォーマンスとスケーラビリティの問題があります。 各行には CategoriesDataSource をデータ ソースとして使用する DropDownList があるため、 CategoriesBLL クラスの GetCategories メソッドはページアクセスごとに n 回呼び出されます 。n は GridView の行数です。 これらの n 個の GetCategories 呼び出しは、データベースに対する n 個 のクエリになります。 このデータベースへの影響は、返されたカテゴリを要求ごとのキャッシュにキャッシュするか、SQL キャッシュ依存関係または非常に短い時間ベースの有効期限を使用してキャッシュ層を介してキャッシュすることで軽減される可能性があります。

手順 4: 編集インターフェイスの完了

進行状況を表示するために一時停止することなく、GridView のテンプレートに多数の変更を加えた。 少し時間を取って、ブラウザーで進捗を確認してみます。 図 13 に示すように、各行はセルの編集インターフェイスを含む ItemTemplateを使用してレンダリングされます。

各 GridView 行は編集可能です

図 13: 各 GridView 行は編集可能です (フルサイズの画像を表示する をクリックします)

この時点で対処する必要がある小さな書式設定の問題がいくつかあります。 最初に、 UnitPrice 値には 4 つの小数点が含まれていることに注意してください。 これを修正するには、 UnitPrice TemplateField の ItemTemplate に戻り、TextBox のスマート タグから [DataBindings の編集] リンクをクリックします。 次に、 Text プロパティを数値として書式設定するように指定します。

Text プロパティを数値として書式設定する

図 14: Text プロパティを数値として書式設定する

次に、(左揃えではなく) Discontinued 列のチェック ボックスを中央に配置します。 GridView のスマート タグから [列の編集] をクリックし、左下隅にあるフィールドの一覧から Discontinued TemplateField を選択します。 図 15 に示すように、 ItemStyle にドリルダウンし、 HorizontalAlign プロパティを Center に設定します。

廃止された CheckBox を中央揃えする

図 15: Discontinued CheckBox を中央揃えする

次に、ValidationSummary コントロールをページに追加し、その ShowMessageBox プロパティを true に設定し、その ShowSummary プロパティを false に設定します。 また、ボタン Web コントロールを追加します。このコントロールをクリックすると、ユーザーの変更が更新されます。 具体的には、GridView の上と下の 2 つのボタン Web コントロールを追加し、両方のコントロール Text プロパティを Update Products に設定します。

GridView の編集インターフェイスは TemplateFields ItemTemplate で定義されているため、 EditItemTemplate は余分であり、削除される可能性があります。

上記の書式設定を変更し、Button コントロールを追加し、不要な EditItemTemplate を削除すると、ページの宣言構文は次のようになります。

<p>
    <asp:Button ID="UpdateAllProducts1" runat="server" Text="Update Products" />
</p>
<p>
    <asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False" 
        DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
        AllowPaging="True" AllowSorting="True">
        <Columns>
            <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
                <ItemTemplate>
                    <asp:TextBox ID="ProductName" runat="server" 
                        Text='<%# Bind("ProductName") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
                        ControlToValidate="ProductName"
                        ErrorMessage="You must provide the product's name." 
                        runat="server">*</asp:RequiredFieldValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Category" 
                SortExpression="CategoryName">
                <ItemTemplate>
                    <asp:DropDownList ID="Categories" runat="server" 
                        AppendDataBoundItems="True" 
                        DataSourceID="CategoriesDataSource"
                        DataTextField="CategoryName" 
                        DataValueField="CategoryID" 
                        SelectedValue='<%# Bind("CategoryID") %>'>
                        <asp:ListItem>-- Select One --</asp:ListItem>
                    </asp:DropDownList>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Price" 
                SortExpression="UnitPrice">
                <ItemTemplate>
                    $<asp:TextBox ID="UnitPrice" runat="server" Columns="8" 
                        Text='<%# Bind("UnitPrice", "{0:N}") %>'></asp:TextBox>
                    <asp:CompareValidator ID="CompareValidator1" runat="server" 
                        ControlToValidate="UnitPrice"
                        ErrorMessage="You must enter a valid currency value. 
                                      Please omit any currency symbols."
                        Operator="GreaterThanEqual" Type="Currency" 
                        ValueToCompare="0">*</asp:CompareValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
                <ItemTemplate>
                    <asp:CheckBox ID="Discontinued" runat="server" 
                        Checked='<%# Bind("Discontinued") %>' />
                </ItemTemplate>
                <ItemStyle HorizontalAlign="Center" />
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
</p>
<p>
    <asp:Button ID="UpdateAllProducts2" runat="server" Text="Update Products" />
    <asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetProducts" TypeName="ProductsBLL">
    </asp:ObjectDataSource>
    <asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetCategories" TypeName="CategoriesBLL">
    </asp:ObjectDataSource>
    <asp:ValidationSummary ID="ValidationSummary1" runat="server" 
        ShowMessageBox="True" ShowSummary="False" />
</p>

図 16 は、ボタン Web コントロールが追加され、書式設定が変更された後にブラウザーで表示された場合のこのページを示しています。

ページに 2 つの更新プログラム製品ボタンが含まれるようになりました

図 16: ページに 2 つの更新プログラム製品ボタンが含まれるようになりました (フルサイズの画像を表示する をクリックします)

手順 5: 製品の更新

ユーザーがこのページにアクセスすると、変更を加え、2 つの [製品の更新] ボタンのいずれかをクリックします。 その時点で、ユーザーが入力した各行の値を何らかの方法で ProductsDataTable インスタンスに保存し、それを BLL メソッドに渡し、その ProductsDataTable インスタンスを DAL の UpdateWithTransaction メソッドに渡す必要があります。 UpdateWithTransactionで作成した メソッドを使用すると、変更のバッチがアトミック操作として更新されます。

BatchUpdateに BatchUpdate.aspx.cs という名前のメソッドを作成し、次のコードを追加します。

private void BatchUpdate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = productsAPI.GetProducts();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Find the ProductsRow instance in products that maps to gvRow
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        Northwind.ProductsRow product = products.FindByProductID(productID);
        if (product != null)
        {
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateWithTransaction(products);
}

このメソッドは、BLL s ProductsDataTable メソッドの呼び出しを使用して、すべての製品をGetProductsに戻することから始めます。 次に、 ProductGrid GridView の Rows コレクションを列挙します。 Rows コレクションには、GridView に表示される各行のGridViewRow インスタンスが含まれています。 1 ページあたり最大 10 行が表示されるため、GridView の Rows コレクションには 10 個以下の項目が含まれます。

各行に対して、 ProductID は DataKeys コレクションから取得され、適切な ProductsRow が ProductsDataTableから選択されます。 4 つの TemplateField 入力コントロールは、プログラムによって参照され、その値が ProductsRow インスタンスのプロパティに割り当てられます。 各GridView行の値がProductsDataTableを更新するために使用された後、その結果がBLLのUpdateWithTransactionメソッドに渡されます。これは、前のチュートリアルで見たように、単にDALのUpdateWithTransactionメソッドを呼び出すだけです。

このチュートリアルで使用されるバッチ更新アルゴリズムは、製品の情報が変更されたかどうかに関係なく、GridView の行に対応する ProductsDataTable の各行を更新します。 通常、このようなブラインド更新はパフォーマンスの問題にはなりませんが、データベース テーブルの変更を監査している場合、余計なレコードが増えてしまう可能性があります。 バッチ更新の実行チュートリアルに戻り、DataList を使用してバッチ更新インターフェイスを調べ、ユーザーが実際に変更したレコードのみを更新するコードを追加しました。 必要に応じて、 バッチ更新を実行する 方法を自由に使用して、このチュートリアルのコードを更新してください。

注

スマート タグを使用してデータ ソースを GridView にバインドすると、Visual Studio によって、データ ソースの主キー値が GridView の DataKeyNames プロパティに自動的に割り当てられます。 手順 1 で説明したように GridView のスマート タグを使用して ObjectDataSource を GridView にバインドしなかった場合は、DataKeyNames コレクションを通じて各行のProductID値にアクセスするために、GridView の DataKeys プロパティを ProductID に手動で設定する必要があります。

BatchUpdateで使用されるコードは、BLL s UpdateProduct メソッドで使用されるコードと似ています。主な違いは、UpdateProduct メソッドではアーキテクチャから 1 つのProductRow インスタンスのみが取得されることです。 ProductRowのプロパティを割り当てるコードは、UpdateProducts メソッドと、foreachのBatchUpdate ループ内のコードの間で、全体的なパターンと同じです。

このチュートリアルを完了するには、[Update Products]\(製品の更新\) ボタンのいずれかがクリックされたときに BatchUpdate メソッドを呼び出す必要があります。 これら 2 つの Button コントロールの Click イベントのイベント ハンドラーを作成し、イベント ハンドラーに次のコードを追加します。

BatchUpdate();
ClientScript.RegisterStartupScript(this.GetType(), "message", 
    "alert('The products have been updated.');", true);

最初に、 BatchUpdateに対して呼び出しが行われます。 次に、 ClientScript property を使用して JavaScript を挿入し、製品が更新されたことを示すメッセージ ボックスを表示します。

このコードをテストするには、少し時間がかかります。 ブラウザーから BatchUpdate.aspx にアクセスし、複数の行を編集して、[Update Products]\(製品の更新\) ボタンのいずれかをクリックします。 入力検証エラーがないと仮定すると、製品が更新されたことを示すメッセージ ボックスが表示されます。 更新プログラムのアトミック性を確認するには、ランダムな CHECK 制約 (1234.56 の UnitPrice 値を許可しない制約など) を追加することを検討してください。 次に、 BatchUpdate.aspxから複数のレコードを編集し、製品の UnitPrice 値の 1 つを許可されていない値 (1234.56) に設定します。 これにより、そのバッチ操作中に他の変更が元の値にロールバックされた状態で [Update Products]\(製品の更新\) をクリックするとエラーが発生します。

代替方法BatchUpdate

ここで調べたBatchUpdateメソッドは、BLL の メソッドからGetProducts製品を取得し、GridView に表示されるレコードのみを更新します。 この方法は、GridView がページングを使用しない場合に最適ですが、使用する場合は、数百、数千、または数万の製品が存在する可能性がありますが、GridView には 10 行しかありません。 このような場合、データベースからすべての製品を取得して、そのうちの 10 個のみを変更することは理想的ではありません。

このような状況の場合は、代わりに次の BatchUpdateAlternate メソッドを使用することを検討してください。

private void BatchUpdateAlternate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Create a new ProductRow instance
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        
        Northwind.ProductsDataTable currentProductDataTable = 
            productsAPI.GetProductByProductID(productID);
        if (currentProductDataTable.Rows.Count > 0)
        {
            Northwind.ProductsRow product = currentProductDataTable[0];
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
            // Import the ProductRow into the products DataTable
            products.ImportRow(product);
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateProductsWithTransaction(products);
}

BatchMethodAlternate最初に、ProductsDataTableという名前の新しい空のproductsを作成します。 次に、GridView の Rows コレクションをステップ実行し、各行に対して BLL s GetProductByProductID(productID) メソッドを使用して特定の製品情報を取得します。 取得したProductsRow インスタンスのプロパティは、BatchUpdateと同じ方法で更新されますが、行を更新した後、DataTable の products``ProductsDataTableを使用してImportRow(DataRow)にインポートされます。

foreach ループが完了すると、productsには GridView の各行に対して 1 つのProductsRow インスタンスが含まれます。 ProductsRowの各インスタンスは (更新ではなく) productsに追加されているため、UpdateWithTransaction メソッドに盲目的に渡すと、ProductsTableAdapterは各レコードをデータベースに挿入しようとします。 代わりに、これらの各行が変更されたことを指定する必要があります (追加されません)。

これを行うには、 UpdateProductsWithTransactionという名前の BLL に新しいメソッドを追加します。 UpdateProductsWithTransaction次に示すように、RowState内の各ProductsRow インスタンスのProductsDataTableをModifiedに設定し、dal のProductsDataTable メソッドにUpdateWithTransactionを渡します。

public int UpdateProductsWithTransaction(Northwind.ProductsDataTable products)
{
    // Mark each product as Modified
    products.AcceptChanges();
    foreach (Northwind.ProductsRow product in products)
        product.SetModified();
    // Update the data via a transaction
    return UpdateWithTransaction(products);
}

概要

GridView には、行ごとの組み込みの編集機能が用意されていますが、完全に編集可能なインターフェイスを作成するためのサポートがありません。 このチュートリアルで説明したように、このようなインターフェイスは可能ですが、少し作業が必要です。 すべての行が編集可能な GridView を作成するには、GridView のフィールドを TemplateFields に変換し、 ItemTemplate 内で編集インターフェイスを定義する必要があります。 さらに、Update All -type Button Web コントロールは、GridView とは別にページに追加する必要があります。 これらの Buttons Click イベント ハンドラーは、GridView の Rows コレクションを列挙し、変更を ProductsDataTableに格納し、更新された情報を適切な BLL メソッドに渡す必要があります。

次のチュートリアルでは、バッチ削除用のインターフェイスを作成する方法について説明します。 具体的には、各 GridView 行にはチェック ボックスが含まれます。[すべての -type を更新] ボタンの代わりに、[選択した行の削除] ボタンがあります。

楽しいプログラミングを!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジを使用しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・セルフ ASP.NET 24時間で2.0です。 彼には mitchell@4GuysFromRolla.comで連絡できます。

特別な感謝を捧げる

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルの校閲者はテレサ・マーフィーとデイヴィッド・スルでした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.comにメッセージを送ってください。