チェックボックスの状態によって、 DB を更新するかしないかを簡単に決める方法

昨日の続きですが、 GridView などにチェックボックスを表示し、チェックされた複数行を一括更新したいときがあります。そんなときは、以下のようなユーティリティメソッドを用意しておくと便利です。

/// <summary>
/// 無駄な更新が発生しないように DataRowState を補正
/// </summary>
/// <param name="table">更新予定の DataTable</param>
/// <param name="checkBoxColumn">チェックボックスの状態を保持した DataColumn (型は bool であること)</param>
/// <remarks>
/// このユーティリティメソッドは、チェックボックスを考慮しながら行の編集状態を調査し、
/// 更新の必要がない DataRow の DataRowState を Unchanged にもどす
/// </remarks>
public static void CorrectDataRowState(DataTable table, DataColumn checkBoxColumn)
{
    foreach (DataRow row in table.Rows)
    {
        if (!HasChanges(row, checkBoxColumn))
            row.RejectChanges();
    }
}

/// <summary>
/// DataRow が変更されているかをチェック
/// </summary>
/// <param name="row">DataRow</param>
/// <param name="checkBoxColumn">チェックボックスの状態を保持した DataColumn (型は bool であること)</param>
/// <returns>変更されていれば true, 変更がなければ false</returns>
private static bool HasChanges(DataRow row, DataColumn checkBoxColumn)
{
    // 追加や削除の場合は無条件で編集あり
    if (row.RowState == DataRowState.Added
        || row.RowState == DataRowState.Deleted
        || row.RowState == DataRowState.Detached)
        return true;

    // チェックがついていないレコードは編集なしとみなす
    // NOTE: DataRowState.Deleted の場合にエラーになるため、
    //       チェックボックスは最後に確認
    if (row.RowState == DataRowState.Unchanged
        || !(bool)row[checkBoxColumn])
        return false;

    // DataRowState.Modified の場合は、各列の内容に変更があるかをチェック
    foreach (DataColumn column in row.Table.Columns)
    {
        // チェックボックス列は比較しない
        if (!(column == checkBoxColumn
            || row[column].Equals(row[column, DataRowVersion.Original])))
            return true;
    }

    // 値が全て同じなら編集していないと見なす
    return false;
}

使用側は、以下のように更新メソッドを呼び出す前に CorrectDataRowState() を呼び出します。

// データを取得
CountryDataTable table = GetData();

// 更新される
table[0].Checked = true;
table[0].Name = "New Name";

// 更新されない (更新対象のチェックボックスが OFF なので)
table[1].Checked = false;
table[1].Name = "New Name";

// 更新されない (Name に同じ値が設定されたため)
table[2].Checked = true;
table[2].Name = table[2].Name;

CorrectDataRowState(table, table.CheckedColumn);

// RowState を考慮しながら table の中身を更新
// NOTE: 通常であれば table[2] の RowState が Modified になる。
//       しかし、 CorrectDataRowState() によって RowState が補正されるため、無駄な更新が発生しない。
Update(table);