概要
.NET の DataTable は、DB を更新しやすいように、トランザクションみたいな概念と、各行の状態(編集あり/編集なし/追加/削除)を保持しています。
トランザクションみたいな概念
- AcceptChange() で全ての DataRow の RowState が「修正なし」になる。
- それ以降に 追加/修正/削除をした場合は、RowState の状態が対応する値に変化する。
- AcceptChange() をすると、「削除」行は消え、「追加」「修正」行は「修正なし」になる。
- RejectChange() をすると、前回、AcceptChange() した状態に戻る。(※一部例外あり)
RowState
RowState | 状態 | 補足 |
---|---|---|
Detached | 未追加 | DataRowを定義したけどDataTableにAddされていない。 |
Unchanged | 編集なし | 編集なし。 |
Added | 追加 | Addedの行を削除すると行そのものが消える。 |
Modified | 編集あり | 編集あり |
Deleted | 削除 | 行としては存在する。AcceptChanges()で消える。 |
動作確認
実行ソース(C#)
static void Main(string[] args)
{
// data
var data = new DataTable();
data.Columns.Add("ID");
for (int i = 0; i < 3; i++)
{
data.Rows.Add(i);
}
data.AcceptChanges();
Console.WriteLine("--------------------");
foreach (DataRow row in data.Rows)
{
Console.WriteLine($"ID:{row["ID"]},RowState={row.RowState}");
}
// 追加
var add = data.NewRow(); // add.RowState:DataRowState.Detached
add["ID"] = 3;
data.Rows.Add(add); // add.RowState:DataRowState.Added
// 修正
var mod = data.Rows[0]; // mod.RowState:DataRowState.Unchanged
mod["ID"] = 9; // mod.RowState:DataRowState.Modified
// 削除
// ・削除された行として存在する。
var del = data.Rows[1]; // del.RowState:DataRowState.Unchanged
del.Delete(); // del.RowState:DataRowState.Deleted
// 除外
// ・DataTableから除外される。
// ・data.RejectChanges();しても、dataから除外されたままになる。
var rmv = data.Rows[2]; // rmv.RowState:DataRowState.Unchanged
data.Rows.Remove(rmv); // rmv.RowState:DataRowState.Detached
Console.WriteLine("--------------------");
foreach (DataRow row in data.Rows)
{
if (row.RowState == DataRowState.Deleted)
{
// 削除された行の値をアクセスしようとする例外が発生する。
// →DataRowVersion.Original を付けて、削除前の状態の値を取得する。
Console.WriteLine($"ID:{row["ID", DataRowVersion.Original]},RowState={row.RowState}");
}
else
{
Console.WriteLine($"ID:{row["ID"]},RowState={row.RowState}");
}
}
// data.AcceptChanges();
// data.RejectChanges();
Console.WriteLine("--------------------");
foreach (DataRow row in data.Rows)
{
Console.WriteLine($"ID:{row["ID"]},RowState={row.RowState}");
}
}
出力結果
--------------------
ID:0,RowState=Unchanged
ID:1,RowState=Unchanged
ID:2,RowState=Unchanged
--------------------
ID:9,RowState=Modified
ID:1,RowState=Deleted
ID:3,RowState=Added
【data.AcceptChanges(); の場合】
--------------------
ID:9,RowState=Unchanged
ID:3,RowState=Unchanged
【data.RejectChanges(); の場合】
--------------------
ID:0,RowState=Unchanged
ID:1,RowState=Unchanged
補足
- Console.WriteLinen内の $"xxxxx:{xxxxx}" のような記述については、『【C#】string.Format() をやめて $"{}"(文字列補間式)を使う - Qiita』を参照。
編集後記
動作を理解していないと思わぬところで Deleted データにアクセスしようとして例外が発生したり、更新対象を正しく取得できなくなってしまいます。
DBのトランザクション管理と同様に、どのタイミングで AcceptChanges(), RejectChanges() するかは、プロジェクトで徹底させないとえらい目に会います。