Share via


バッチ更新を実行する (C#)

作成者: Scott Mitchell

PDF のダウンロード

すべてのアイテムが編集モードで、ページの [すべて更新] ボタンをクリックして値を保存できる、完全に編集可能な DataList を作成する方法について説明します。

はじめに

前の チュートリアル では、項目レベルの DataList を作成する方法を調べました。 標準の編集可能な GridView と同様に、DataList の各項目には[編集]ボタンが含まれており、クリックするとアイテムが編集可能になります。 この項目レベルの編集は、時折更新されるデータに対しては適切に機能しますが、特定のユース ケースシナリオでは、ユーザーが多くのレコードを編集する必要があります。 ユーザーが何十ものレコードを編集する必要があり、強制的に [編集] をクリックして変更を加え、それぞれに対して [更新] をクリックすると、クリックの量が生産性を低下させる可能性があります。 このような状況では、完全に編集可能な DataList を提供することをお勧めします。この DataList では、 すべての アイテムが編集モードで、ページの [すべて更新] ボタンをクリックして値を編集できます (図 1 を参照)。

完全に編集可能な DataList 内の各項目は変更可能

図 1: 完全に編集可能な DataList 内の各項目を変更できます (フルサイズの画像を表示する場合はクリックします)

このチュートリアルでは、ユーザーが完全に編集可能な DataList を使用してサプライヤーの住所情報を更新できるようにする方法について説明します。

手順 1: DataList の ItemTemplate で編集可能なユーザー インターフェイスを作成する

前のチュートリアルでは、項目レベルの編集可能な標準の DataList を作成し、次の 2 つのテンプレートを使用しました。

  • ItemTemplate には、読み取り専用ユーザー インターフェイス (各製品の名前と価格を表示するためのラベル Web コントロール) が含まれていました。
  • EditItemTemplate には、編集モードのユーザー インターフェイス (2 つの TextBox Web コントロール) が含まれていました。

DataList の EditItemIndex プロパティは、 を使用してレンダリングされる内容 DataListItem (存在する場合) を指定します EditItemTemplate。 特に、DataListItemDataList の EditItemIndex プロパティと一致する値を持ItemIndexつ は、 をEditItemTemplate使用してレンダリングされます。 このモデルは、一度に 1 つの項目しか編集できないが、完全に編集可能な DataList を作成するときに分解される場合に適切に機能します。

完全に編集可能な DataList の場合は、編集可能なインターフェイスをDataListItem使用して、すべての がレンダリングされるようにします。 これを実現する最も簡単な方法は、 で編集可能なインターフェイスを ItemTemplate定義することです。 仕入先住所情報を変更する場合、編集可能なインターフェイスには、サプライヤー名がテキストとして、住所、市区町村、国/地域の値の TextBox が含まれます。

まず、ページを BatchUpdate.aspx 開き、DataList コントロールを追加し、そのプロパティを IDSuppliers設定します。 DataList のスマート タグから、 という名前 SuppliersDataSourceの新しい ObjectDataSource コントロールを追加することを選択します。

SuppliersDataSource という名前の新しい ObjectDataSource を作成する

図 2: 名前付きの SuppliersDataSource 新しい ObjectDataSource を作成する (フルサイズの画像を表示する場合はクリックします)

クラス s GetSuppliers() メソッドを使用してデータを取得するように ObjectDataSource をSuppliersBLL構成します (図 3 を参照)。 前のチュートリアルと同様に、ObjectDataSource を使用してサプライヤー情報を更新するのではなく、ビジネス ロジック レイヤーを直接操作します。 そのため、[更新] タブでドロップダウン リストを (なし) に設定します (図 4 を参照)。

GetSuppliers() メソッドを使用してサプライヤー情報を取得する

図 3: メソッドを使用して仕入先情報を取得する GetSuppliers() (クリックするとフルサイズの画像が表示されます)

[更新] タブで Drop-Down リストを (なし) に設定します

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

ウィザードが完了すると、Visual Studio によって DataList が ItemTemplate 自動的に生成され、データ ソースによって返された各データ フィールドがラベル Web コントロールに表示されます。 代わりに編集インターフェイスが提供されるように、このテンプレートを変更する必要があります。 はItemTemplate、DataList のスマート タグの [テンプレートの編集] オプションを使用するか、宣言構文を使用して直接、Designerを使用してカスタマイズできます。

少し時間を取って、サプライヤーの名前をテキストとして表示する編集インターフェイスを作成しますが、仕入先の住所、市区町村、国/地域の値の TextBox が含まれます。 これらの変更を行った後、ページの宣言構文は次のようになります。

<asp:DataList ID="Suppliers" runat="server" DataKeyField="SupplierID"
    DataSourceID="SuppliersDataSource">
    <ItemTemplate>
        <h4><asp:Label ID="CompanyNameLabel" runat="server"
            Text='<%# Eval("CompanyName") %>' /></h4>
        <table border="0">
            <tr>
                <td class="SupplierPropertyLabel">Address:</td>
                <td class="SupplierPropertyValue">
                    <asp:TextBox ID="Address" runat="server"
                        Text='<%# Eval("Address") %>' />
                </td>
            </tr>
            <tr>
                <td class="SupplierPropertyLabel">City:</td>
                <td class="SupplierPropertyValue">
                    <asp:TextBox ID="City" runat="server"
                        Text='<%# Eval("City") %>' />
                </td>
            </tr>
            <tr>
                <td class="SupplierPropertyLabel">Country:</td>
                <td class="SupplierPropertyValue">
                    <asp:TextBox ID="Country" runat="server"
                        Text='<%# Eval("Country") %>' />
                </td>
            </tr>
        </table>
        <br />
    </ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
</asp:ObjectDataSource>

注意

前のチュートリアルと同様に、このチュートリアルの DataList では、ビューステートを有効にする必要があります。

では、ItemTemplateSupplierPropertyLabel 2 つの新しい CSS クラス と SupplierPropertyValueを使用します。これは、 クラスにStyles.css追加され、 クラスと ProductPropertyValue CSS クラスと同じスタイル設定をProductPropertyLabel使用するように構成されています。

.ProductPropertyLabel, .SupplierPropertyLabel
{
    font-weight: bold;
    text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
    padding-right: 35px;
}

これらの変更を行った後、ブラウザーからこのページにアクセスします。 図 5 に示すように、各 DataList 項目にはサプライヤー名がテキストとして表示され、TextBoxes を使用して住所、市区町村、国/地域が表示されます。

DataList の各サプライヤーは編集可能です

図 5: DataList 内の各サプライヤーは編集可能です (フルサイズの画像を表示する場合はクリックします)

手順 2: [すべて更新] ボタンを追加する

図 5 の各サプライヤーには、住所、市区町村、国/地域のフィールドが TextBox に表示されていますが、現在、[更新] ボタンはありません。 アイテムごとに [更新] ボタンを使用するのではなく、完全に編集可能な DataList を使用すると、通常、ページ上に 1 つの [すべて更新] ボタンがあり、クリックすると DataList 内のすべての レコードが更新されます。 このチュートリアルでは、2 つの [すべて更新] ボタン (ページの上部に 1 つ、下部に 1 つ) を追加します (ただし、いずれかのボタンをクリックすると同じ効果が得られます)。

まず、DataList の上に Button Web コントロールを追加し、そのプロパティを IDUpdateAll1設定します。 次に、DataList の下に 2 つ目の Button Web コントロールを追加し、 を ID に設定します UpdateAll2。 2 つのボタンの Text プロパティを [すべて更新] に設定します。 最後に、両方の Buttons イベントのイベント ハンドラーを Click 作成します。 各イベント ハンドラーで更新ロジックを複製するのではなく、そのロジックを 3 番目のメソッド () にリファクタリングし、 UpdateAllSupplierAddressesイベント ハンドラーがこの 3 番目のメソッドを呼び出すだけです。

protected void UpdateAll1_Click(object sender, EventArgs e)
{
    UpdateAllSupplierAddresses();
}
protected void UpdateAll2_Click(object sender, EventArgs e)
{
    UpdateAllSupplierAddresses();
}
private void UpdateAllSupplierAddresses()
{
    // TODO: Write code to update _all_ of the supplier addresses in the DataList
}

図 6 は、[すべて更新] ボタンが追加された後のページを示しています。

2 つの [すべての更新] ボタンがページに追加されました

図 6: 2 つの [すべての更新] ボタンがページに追加されました (クリックするとフルサイズの画像が表示されます)

手順 3: すべての仕入先住所情報を更新する

編集インターフェイスが表示され、[すべて更新] ボタンが追加された DataList のすべての項目で、残っているのはバッチ更新を実行するコードを記述することです。 具体的には、DataList の項目をループ処理し、それぞれに対してクラス s UpdateSupplierAddress メソッドをSuppliersBLL呼び出す必要があります。

DataList を構成するインスタンスの DataListItem コレクションには、DataList の Items プロパティを使用してアクセスできます。 へのDataListItem参照を使用すると、次のコードに示すように、 コレクションからDataKeys対応する SupplierID を取得し、 内ItemTemplateの TextBox Web コントロールをプログラムで参照できます。

private void UpdateAllSupplierAddresses()
{
    // Create an instance of the SuppliersBLL class
    SuppliersBLL suppliersAPI = new SuppliersBLL();
    // Iterate through the DataList's items
    foreach (DataListItem item in Suppliers.Items)
    {
        // Get the supplierID from the DataKeys collection
        int supplierID = Convert.ToInt32(Suppliers.DataKeys[item.ItemIndex]);
        // Read in the user-entered values
        TextBox address = (TextBox)item.FindControl("Address");
        TextBox city = (TextBox)item.FindControl("City");
        TextBox country = (TextBox)item.FindControl("Country");
        string addressValue = null, cityValue = null, countryValue = null;
        if (address.Text.Trim().Length > 0)
            addressValue = address.Text.Trim();
        if (city.Text.Trim().Length > 0)
              cityValue = city.Text.Trim();
        if (country.Text.Trim().Length > 0)
            countryValue = country.Text.Trim();
        // Call the SuppliersBLL class's UpdateSupplierAddress method
        suppliersAPI.UpdateSupplierAddress
            (supplierID, addressValue, cityValue, countryValue);
    }
}

ユーザーが [すべて更新] ボタンのいずれかをクリックすると、メソッドは UpdateAllSupplierAddresses DataList 内の各DataListItemボタンをSuppliers反復処理し、クラスの メソッドUpdateSupplierAddressSuppliersBLL呼び出して、対応する値を渡します。 住所、市区町村、または国/地域のパスに入力されていない値は、(空白の Nothing 文字列ではなく) の UpdateSupplierAddress 値であり、基になるレコードのフィールドのデータベース NULL になります。

注意

機能強化として、バッチ更新の実行後に確認メッセージが表示される状態ラベル Web コントロールをページに追加できます。

変更されたアドレスのみを更新する

このチュートリアルで使用するバッチ更新アルゴリズムは、住所情報が UpdateSupplierAddress 変更されているかどうかに関係なく、DataList 内のすべての サプライヤーに対して メソッドを呼び出します。 このようなブラインド更新は通常、パフォーマンスの問題ではありませんが、データベース テーブルの変更を監査すると、余分なレコードが発生する可能性があります。 たとえば、トリガーを使用してテーブルのすべての UPDATE を監査テーブルに Suppliers 記録する場合、ユーザーが [すべて更新] ボタンをクリックするたびに、ユーザーが変更を加えたかどうかに関係なく、システム内のサプライヤーごとに新しい監査レコードが作成されます。

ADO.NET DataTable クラスと DataAdapter クラスは、変更、削除、および新しいレコードのみがデータベース通信の結果となるバッチ更新をサポートするように設計されています。 DataTable の各行には RowState 行が DataTable に追加されたか、その行から削除されたか、変更されたか、変更されていないかを示すプロパティがあります。 DataTable が最初に設定されると、すべての行が変更されずにマークされます。 行の列の値を変更すると、行が変更済みとしてマークされます。

クラスでは、SuppliersBLL最初に 1 つの仕入先レコードを に読み取り、次にSuppliersDataTable、次のコードを使用して 、City、および Country 列の値をAddress設定することで、指定した仕入先の住所情報を更新します。

public bool UpdateSupplierAddress
    (int supplierID, string address, string city, string country)
{
    Northwind.SuppliersDataTable suppliers =
        Adapter.GetSupplierBySupplierID(supplierID);
    if (suppliers.Count == 0)
        // no matching record found, return false
        return false;
    else
    {
        Northwind.SuppliersRow supplier = suppliers[0];
        if (address == null)
            supplier.SetAddressNull();
        else
            supplier.Address = address;
        if (city == null)
            supplier.SetCityNull();
        else
            supplier.City = city;
        if (country == null)
            supplier.SetCountryNull();
        else
            supplier.Country = country;
        // Update the supplier Address-related information
        int rowsAffected = Adapter.Update(supplier);
        // Return true if precisely one row was updated,
        // otherwise false
        return rowsAffected == 1;
    }
}

このコードでは、値が変更されたかどうかに関係なく、渡された住所、市区町村、国/地域 SuppliersRow の値を の に SuppliersDataTable 単純に割り当てます。 これらの変更により、 プロパティがSuppliersRowRowState変更済みとしてマークされます。 データ アクセス層の Update メソッドが呼び出されると、 が変更されたことが確認 SupplierRow され、コマンドがデータベースに送信 UPDATE されます。

ただし、渡された住所、市区町村、国/地域の値が既存の値と異なる場合にのみ割り当てるために、このメソッドにコードを SuppliersRow 追加したとします。 住所、市区町村、国/地域が既存のデータと同じである場合、変更は行SupplierRowRowStateわれず、s は変更されていないものとしてマークされます。 結果として、DAL の Update メソッドが呼び出されると、 が変更されていないため SuppliersRow 、データベース呼び出しは行われません。

この変更を適用するには、渡された住所、市区町村、国/地域の値を盲目的に割り当てるステートメントを次のコードに置き換えます。

// Only assign the values to the SupplierRow's column values if they differ
if (address == null && !supplier.IsAddressNull())
    supplier.SetAddressNull();
else if ((address != null && supplier.IsAddressNull()) ||
         (!supplier.IsAddressNull() &&
         string.Compare(supplier.Address, address) != 0))
    supplier.Address = address;
if (city == null && !supplier.IsCityNull())
    supplier.SetCityNull();
else if ((city != null && supplier.IsCityNull()) ||
         (!supplier.IsCityNull() && string.Compare(supplier.City, city) != 0))
    supplier.City = city;
if (country == null && !supplier.IsCountryNull())
    supplier.SetCountryNull();
else if ((country != null && supplier.IsCountryNull()) ||
         (!supplier.IsCountryNull() &&
         string.Compare(supplier.Country, country) != 0))
    supplier.Country = country;

このコードを追加すると、DAL メソッド Update は、アドレス関連の値が変更されたレコードに対してのみ、ステートメントをデータベースに送信 UPDATE します。

または、渡されたアドレス フィールドとデータベース データに違いがあるかどうかを追跡し、存在しない場合は DAL メソッド Update の呼び出しをバイパスします。 この方法は、DB ダイレクト メソッドを使用している場合に適切に機能します。これは、データベース呼び出しがRowState実際に必要かどうかを確認できるインスタンスが DB ダイレクト メソッドに渡SuppliersRowされないためです。

注意

メソッドが UpdateSupplierAddress 呼び出されるたびに、更新されたレコードに関する情報を取得する呼び出しがデータベースに対して行われます。 その後、データに変更がある場合は、テーブル行を更新するためにデータベースの別の呼び出しが行われます。 このワークフローは、ページからBatchUpdate.aspxのすべての変更を含むインスタンスをUpdateSupplierAddressEmployeesDataTable受け入れるメソッド オーバーロードを作成することで最適化できます。 その後、データベースを 1 回呼び出して、テーブルからすべてのレコードを Suppliers 取得できます。 その後、2 つの結果セットを列挙し、変更が発生したレコードのみを更新できます。

まとめ

このチュートリアルでは、完全に編集可能な DataList を作成し、ユーザーが複数のサプライヤーの住所情報をすばやく変更できるようにする方法について説明しました。 まず、DataList ItemTemplateの の仕入先の住所、市区町村、国/地域の値に対して TextBox Web コントロールを編集インターフェイスとして定義することから始めました。 次に、DataList の上下に [すべて更新] ボタンを追加しました。 ユーザーが自分の変更を行い、[すべて更新] ボタンのいずれかをクリックすると、DataListItems が列挙され、クラス s UpdateSupplierAddress メソッドのSuppliersBLL呼び出しが行われます。

プログラミングに満足!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジと協力しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Zack Jones と Ken Pespisa でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。