DataRowクラス

DataTableのデータの行を表します。このクラスのインスタンスは、DataTable.Rows.Add()またはDataTable.NewRow()などから作成できます。

プロパティ

プロパティ 内容
object Item[DataColumn] 指定のDataColumnに格納されているデータ
object Item[String] 指定の名前の列に格納されているデータ
object Item[Int32] 指定のインデックスの列に格納されているデータ
object Item[Int32, DataRowVersion] 指定のインデックスの列に格納されている、指定のバージョンのデータ
object[] ItemArray 行のすべての値
DataTable Table この行のスキーマがあるDataTable。これはこの行がDataTableのコレクションに属しているかは無関係であり、それを知りたくばRowStateがDetachedであるかどうかで判別する
DataRowState RowState 行の状態
string RowError 行のカスタムエラー
bool HasErrors trueならば、エラーが含まれている。個別の項目にエラーが含まれているか、RowErrorプロパティが空文字列ではないならば、trueとなる
     
Properties - DataRow Class (System.Data) | Microsoft Learn

DataTableに含まれていないDataRow、つまりRowStateがDetachedの行を処理しようとすると、RowNotInTableExceptionが投げられます。

Item[]

引数でDataRowVersionも指定することで、異なる状態の値を取得できます。DataRow.Item[String, DataRowVersion] Property (System.Data) | Microsoft Learn

それを指定しない場合は、Defaultの状態の値となります。this - DataRow.cs

Stringで列を指定するときは、大文字/小文字は区別されません。

RowStateの状態によっては、次の例外が投げられます。

DataRowVersion 列挙型
列挙子 数値 意味
Current 512 現在の値
Original 256 初期値

(最初に設定した値ではなく、編集前の値の意味。AcceptChanges()を呼ぶと、このOriginalがCurrentの値に更新される)

Proposed 1024 提案値 (proposed value)

(編集を確定する前の値)

Default 1536 DataRowStateの値によって異なる
  • Added、ModifiedまたはDeletedならば、Current
  • Detached、Unchangedならば、Proposed
DataRowVersion Enum (System.Data) | Microsoft Learn

DataRowVersionは、次の状況で変更されます。Remarks - DataRowVersion Enum (System.Data) | Microsoft Learn

  • DataRow.BeginEdit()を呼び出した後に値を変更すると、CurrentとProposedの値が有効になる
  • DataRow.CancelEdit()を呼び出した後、Proposedの値は削除される
  • DataRow.EndEdit()を呼び出した後、Proposedの値がCurrentの値となる
  • DataRow.AcceptChanges()またはDataTable.AcceptChanges()を呼び出した後、Originalの値がCurrentと同一となる
  • DataRow.RejectChanges()を呼び出した後、Proposedの値は破棄され、バージョンはCurrentとなる
DataTable table = new DataTable();
table.Columns.Add();

DataRow row = table.NewRow();
DataRowState state = row.RowState; // Detached

row[0] = 1;

table.Rows.Add(row);
state = row.RowState; // Added

object current, original, proposed;
current = row[0, DataRowVersion.Current];      // "1"
// original = row[0, DataRowVersion.Original]; // VersionNotFoundException
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException

row.AcceptChanges();
state = row.RowState; // Unchanged

current = row[0, DataRowVersion.Current];      // "1"
original = row[0, DataRowVersion.Original];    // "1"
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException

row.BeginEdit(); // 編集を開始する
state = row.RowState; // Unchanged

current = row[0, DataRowVersion.Current];   // "1"
original = row[0, DataRowVersion.Original]; // "1"
proposed = row[0, DataRowVersion.Proposed]; // "1"

row[0] = 2; // 値を操作する
state = row.RowState; // Unchanged

current = row[0, DataRowVersion.Current];   // "1"
original = row[0, DataRowVersion.Original]; // "1"
proposed = row[0, DataRowVersion.Proposed]; // "2"

row.EndEdit(); // 編集を終了する
state = row.RowState; // Modified

current = row[0, DataRowVersion.Current];      // "2"
original = row[0, DataRowVersion.Original];    // "1"
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException

row[0] = 3; // 値を操作する
state = row.RowState; // Modified

row.AcceptChanges(); // 変更を確定する
state = row.RowState; // Unchanged

current = row[0, DataRowVersion.Current];      // "3"
original = row[0, DataRowVersion.Original];    // "3"
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException

row.Delete(); // 行を削除する
state = row.RowState; // Deleted

// current = row[0, DataRowVersion.Current];   // VersionNotFoundException
original = row[0, DataRowVersion.Original];    // "3"
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException

// row[0] = 4; // DeletedRowInaccessibleException

row.AcceptChanges(); // 変更を確定する
state = row.RowState; // Detached

// current = row[0, DataRowVersion.Current];   // VersionNotFoundException
// original = row[0, DataRowVersion.Original]; // VersionNotFoundException
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException

RowState

DataRowState 列挙型
列挙子 数値 意味
Detached 1 行は作成されているが、コレクションの一部ではない。これはコレクションへの追加前か除去後の状態
Unchanged 2 AcceptChanges()が呼ばれてから、行は変更されていない
Added 4 行はコレクションに追加されているが、AcceptChanges()は呼ばれていない
Deleted 8 行はDelete()で削除されているが、AcceptChanges()は呼ばれていない
Modified 16 行は変更されているが、AcceptChanges()は呼ばれていない
DataRowState Enum (System.Data) | Microsoft Learn

DBNullの判定

AllowDBNullがtrueの列にはnullが格納されることがあります。しかしその値は実際にはnullとは異なるため、

row[column] == null

としても判定できず、

row[column] == DBNull.Value

のようにSystem.DBNullクラスのValueフィールドと比較する必要があります。これはConvertクラスのメソッドを用いて

Convert.IsDBNull(row[column])

とすることでも可能です。なお、このConvertクラスのDBNullフィールドは、DBNull.Valueです。

またはDataRowのIsNull()メソッドを用いて、

row.IsNull(column)

として判定する方法もあります。DataRow.IsNull Method (System.Data) | Microsoft Learn

DataTable table = new DataTable();
DataColumn column = table.Columns.Add();
DataRow row = table.Rows.Add();

row.SetField(column, DBNull.Value);

bool r1 = row[column] == null;           // false
bool r2 = row[column] == DBNull.Value;   // true
bool r3 = Convert.IsDBNull(row[column]); // true
bool r4 = row.IsNull(column);            // true

RowError

未設定ならば、空文字列が返されます。未設定のときnullまたは空文字列を指定しても、それは設定されません。以降はnullを指定すると空文字列が、それ以外はその文字列が設定されます。DataRow.rowerror

個別の項目にエラーを設定するには、SetColumnError()を使用する。

エラーを変更しても、RowStateは変更されません。

DataTable table = new DataTable();
table.Columns.Add();

DataRow row = table.Rows.Add("A");
row.AcceptChanges();
DataRowState state = row.RowState; // Unchanged

row.RowError = "error";
state = row.RowState; // Unchanged

row.SetColumnError(0, "error");
state = row.RowState; // Unchanged

メソッド

メソッド 機能
GetChildRows(String) DataRowの子の行を取得できる
GetParentRow(String) DataRowの、1つの親の行を取得できる
GetParentRows(String) DataRowの、複数の親の行を取得できる
SetParentRow(DataRow) DataRowに、親の行を設定できる
HasVersion(DataRowVersion) 指定のバージョンが存在するか確認できる
IsNull(DataColumn) 指定の列がnullを含むか確認できる
BeginEdit() 編集モードにできる
CancelEdit() 編集をキャンセルできる
EndEdit() 編集を終了できる
AcceptChanges() 最後にAcceptChanges()を呼んだ後の変更をコミットできる。EndEdit()が暗黙的に呼ばれ、編集モードが終わる

RowStateがAddedまたはModifiedならばUnchangedになり、Deletedならば行は除去される

RejectChanges() 最後にAcceptChanges()を呼んだ後の変更を拒否できる。CancelEdit()が暗黙的に呼ばれ、編集がキャンセルされる

RowStateがdeletedまたはmodifiedならばunchangedになり前の値に戻させる。またaddedならば行は除去される

SetAdded() RowStateを、Addedに変更できる。これを呼べるのはRowStateがUnchangedのときのみで、現在のデータが初期値に設定される
SetModified() RowStateを、Modifiedに変更できる。これを呼べるのはRowStateがUnchangedのときのみで、コミットしていないデータは初期値に戻される
Delete() RowStateを、Deletedに変更できる。RowStateがAddedならば除去されるが、そうでなければAcceptChanges()が呼ばれたときに除去される。
SetNull(DataColumn) (protected) 指定の列を、null値に設定する。これはItemプロパティからDBNull.Valueを設定するのと同義 SetNull - DataRow.cs
SetColumnError(DataColumn, String) 列のエラーの説明を設定できる。エラーを消すには、nullまたは空文字列を設定する
GetColumnError(DataColumn) 列のエラーの説明を取得できる
GetColumnsInError() エラーのある列の配列を取得できる
ClearErrors() 行のエラーを消去できる。これにはRowErrorプロパティと、SetColumnError()で設定したエラーが含まれる
   
Methods - DataRow Class (System.Data) | Microsoft Learn
拡張メソッド
メソッド 機能
Field<T>(DataRow, String) 指定の列を、指定の型で取得できる
SetField<T>(DataRow, String, T) 指定の列に、新しい値を設定できる
   
Extension Methods - DataRow Class (System.Data) | Microsoft Learn

GetChildRows()

DataRelationが設定された、子の行を取得できます。

public System.Data.DataRow[] GetChildRows (string relationName);
GetChildRows(String) - DataRow.GetChildRows Method (System.Data) | Microsoft Learn

DataRowVersionを省略すると、DataRowVersion.Defaultが指定されます。

子の行が存在しないときは、長さゼロの配列が返されます。

DataSet dataSet = new DataSet();
DataTable tableA = dataSet.Tables.Add();
DataTable tableB = dataSet.Tables.Add();

DataRelation relation = dataSet.Relations.Add(
    tableA.Columns.Add(),
    tableB.Columns.Add());

DataRow rowA = tableA.Rows.Add("1");
DataRow rowB = tableB.Rows.Add("1");


DataRow[] childRows = rowA.GetChildRows(relation);

DataRow childRow = childRows.Single();
bool equals = Object.ReferenceEquals(childRow, rowB); // true

削除された行の子の行を取得しようとすると、DeletedRowInaccessibleExceptionが投げられます。このような場合には、DataRowVersionを指定して取得します。

row.Delete();

// DataRow[] rows = row.GetChildRows(relation); // DeletedRowInaccessibleException
DataRow[] rows = row.GetChildRows(relation, DataRowVersion.Original);

内部ではIndex.GetRows()で列挙子が用いられているため、このメソッドを呼び出しているときに別スレッドから子となる行を操作すると、「コレクションが修正されました。列挙操作が実行されない可能性があります。」としてInvalidOperationExceptionが投げられます。

GetParentRow()

親の行を取得できます。

public System.Data.DataRow GetParentRow (string relationName);
GetParentRow(String) - DataRow.GetParentRow Method (System.Data) | Microsoft Learn

DataRowVersionを省略すると、DataRowVersion.Defaultが指定されます。

DataRowのRowStateがDeletedのとき、GetParentRows()ではDeletedRowInaccessibleExceptionが投げられますが、このメソッドではnullが返されます。

BeginEdit()

DataRowの編集操作を開始できます。この編集モードではDataRowを操作してもイベントは発生しなくなります。

DataTable table = new DataTable();
table.Columns.Add();
table.Columns.Add();

table.RowChanged += (object sender, DataRowChangeEventArgs e) => { };

DataRow row1 = table.Rows.Add(); // RowChangedが発生し、そのe.actionは Add
row1[0] = 1; // Change
row1[1] = 2; // Change
row1.AcceptChanges(); // Commit

DataRow row2 = table.Rows.Add(); // Add
row2.BeginEdit();
row2[0] = 1; // RowChangedは発生しない
row2[1] = 2;
row2.AcceptChanges(); // Change、Commitと RowChangedが2回発生する
  • BeginEdit()で開始した編集をCancelEdit()でキャンセルすると、データとRowStateは開始前の状態に戻る
  • BeginEdit()で開始した編集をEndEdit()で終了すると、データは確定する。RowStateがAddedならそのまま、UnchangedならModifiedとなる
DataTable table = new DataTable();
table.Columns.Add();

DataRow row = table.Rows.Add(1);
DataRowState state = row.RowState; // Added

object current, original, proposed;
current = row[0, DataRowVersion.Current];      // "1"
// original = row[0, DataRowVersion.Original]; // VersionNotFoundException
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException


// Begin - Cancel
row.BeginEdit();
state = row.RowState; // Added

row[0] = 2; //
state = row.RowState; // Added

current = row[0, DataRowVersion.Current];      // "1"
// original = row[0, DataRowVersion.Original]; // VersionNotFoundException
proposed = row[0, DataRowVersion.Proposed];    // "2"

row.CancelEdit();
state = row.RowState; // Added

current = row[0, DataRowVersion.Current];      // "1"
// original = row[0, DataRowVersion.Original]; // VersionNotFoundException
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException


row.AcceptChanges();
state = row.RowState; // Unchanged

current = row[0, DataRowVersion.Current];      // "1"
original = row[0, DataRowVersion.Original];    // "1"
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException


// Begin - Cancel
row.BeginEdit();
state = row.RowState; // Unchanged

row[0] = 2; //
state = row.RowState; // Unchanged

current = row[0, DataRowVersion.Current];   // "1"
original = row[0, DataRowVersion.Original]; // "1"
proposed = row[0, DataRowVersion.Proposed]; // "2"

row.CancelEdit();
state = row.RowState; // Unchanged

current = row[0, DataRowVersion.Current];      // "1"
original = row[0, DataRowVersion.Original];    // "1"
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException


// Begin - End
row.BeginEdit();
state = row.RowState; // Unchanged

row[0] = 2; //
state = row.RowState; // Unchanged

current = row[0, DataRowVersion.Current];   // "1"
original = row[0, DataRowVersion.Original]; // "1"
proposed = row[0, DataRowVersion.Proposed]; // "2"

row.EndEdit();
state = row.RowState; // Modified

current = row[0, DataRowVersion.Current];   // "2"
original = row[0, DataRowVersion.Original]; // "1"
// proposed = row[0, DataRowVersion.Proposed]; // VersionNotFoundException

AcceptChanges()

最後にAcceptChanges()を呼んだ後の変更をコミットできます。

public void AcceptChanges ();
DataRow.AcceptChanges Method (System.Data) | Microsoft Learn

既定では子の行へは作用しません。これはForeignKeyConstraint.AcceptRejectRuleをCascadeに設定することで変更できます。AcceptChanges and RejectChanges - ADO.NET | Microsoft Learn

Delete()

public void Delete ();
DataRow.Delete Method (System.Data) | Microsoft Learn

Delete()の呼び出し時にRowStateがAddedならばDataTable.Rowsから除去されDetachedとなりますが、そうでなければDeletedとなりAcceptChanges()が呼ばれたときに除去されDetachedとなります。これを無条件に実行したいならば、DataRowCollection.Remove()を呼びます。

DataTable table = new DataTable();


DataRow row1 = table.Rows.Add();
Console.WriteLine($"{row1.RowState} : {table.Rows.Count}"); // Added : 1

row1.Delete(); // DataTable.Rowsから除去される
Console.WriteLine($"{row1.RowState} : {table.Rows.Count}"); // Detached : 0


DataRow row2 = table.Rows.Add();
row2.AcceptChanges();
Console.WriteLine($"{row2.RowState} : {table.Rows.Count}"); // Unchanged : 1

row2.Delete(); // 削除済みとマークされるが、DataTable.Rowsに残っている
Console.WriteLine($"{row2.RowState} : {table.Rows.Count}"); // Deleted : 1

row2.AcceptChanges(); // ここでDataTable.Rowsから除去される
Console.WriteLine($"{row2.RowState} : {table.Rows.Count}"); // Detached : 0

RowStateがDeletedとなった状態からは、RejectChanges()により呼び出し前の状態に戻せます。

DataRow row3 = table.Rows.Add();
row3.AcceptChanges();
Console.WriteLine($"{row3.RowState} : {table.Rows.Count}"); // Unchanged : 1

row3.Delete();
Console.WriteLine($"{row3.RowState} : {table.Rows.Count}"); // Deleted : 1

row3.RejectChanges(); // Delete()の呼び出し前に戻せる
Console.WriteLine($"{row3.RowState} : {table.Rows.Count}"); // Unchanged : 1

row3.Delete(); // Deleted
row3.AcceptChanges(); // Detached
row3.RejectChanges(); // Detached. もう戻らない

Delete()はそのDataRowが含まれるDataRowCollectionの状態を変更するため、そのコレクションのforeachループ内から呼んではなりません。Remarks - DataRow.Delete Method (System.Data) | Microsoft Learn

DataTable table = new DataTable();
table.Rows.Add();
table.Rows.Add();

foreach (DataRow row in table.Rows) // 2回目のループで、InvalidOperationException「コレクションが修正されました。列挙操作が実行されない可能性があります。」が投げられる
{
    row.Delete();
}

Relationが設定された子テーブルがあるならば、その子行へも作用します。

DataSet dataSet = new DataSet();
DataTable tableA = dataSet.Tables.Add();
DataTable tableB = dataSet.Tables.Add();

DataRelation relation = dataSet.Relations.Add(
    tableA.Columns.Add(),
    tableB.Columns.Add());

DataRow rowA = tableA.Rows.Add("1");
DataRow rowB = tableB.Rows.Add("1");

rowA.Delete();

DataRowState stateA = rowA.RowState; // Detached
DataRowState stateB = rowB.RowState; // Detached

ただしForeignKeyConstraint.DeleteRuleが既定から変更されているときには、その設定に従います。

...
relation.ChildKeyConstraint.DeleteRule = Rule.SetDefault;

rowA.AcceptChanges();
rowB.AcceptChanges();

rowA.Delete();

DataRowState stateA = rowA.RowState; // Deleted
DataRowState stateB = rowB.RowState; // Modified

Field<T>()

指定の列を、指定の型で取得できます。このメソッドはSystem.Data.DataSetExtensionsで定義されているため、利用するにはこれを参照に追加する必要があります。

public static T Field<T> (
    this System.Data.DataRow row,
    string columnName
    );
Field<T>(DataRow, String) - DataRowExtensions.Field Method (System.Data) | Microsoft Learn

このメソッドは次のように実装されています。

public static T Field<T>(this DataRow row, string columnName) {
    DataSetUtil.CheckArgumentNull(row, "row"); // rowがnullならば、ArgumentNullExceptionが投げられる
    return UnboxT<T>.Unbox(row[columnName]);
}
Field - DataRowExtensions.cs

指定の型への変換に失敗したときには、InvalidCastExceptionが投げられます。これは列のデータがnullのとき、指定の型がNULL許容型ではない場合も同様です。この、データがnullのときの挙動が、Item[]プロパティと異なります。

DataTable table = new DataTable();
table.Columns.Add("col1", typeof(object));
table.Columns.Add("col2", typeof(int));

DataRow row = table.NewRow();

// 指定の列のデータがnullのとき
object val1 = row["col1"]; // DBNull が返される 
object val2 = row["col2"]; // DBNull が返される

object val3 = row.Field<object>("col1"); // null が返される
object val4 = row.Field<int>("col2");    // InvalidCastException が投げられる
データがnullのときの、戻り値の差異
  列の型
  object int
Item[] DBNull DBNull
Field<T>() null InvalidCastException

列をDataColumnで指定するオーバーロードでは、それにnullを指定すると「'column' 引数を Null にすることはできません。」としてArgumentNullExceptionが投げられます。

DataTable table = new DataTable("table");
DataColumn col1 = table.Columns.Add("col1", typeof(object));
DataColumn col2 = new DataColumn("col2");
DataColumn col3 = null;

DataRow row = table.NewRow();

object val1 = row[col1]; // DBNull
object val2 = row[col2]; // ArgumentException「列 'col2' はテーブル table に属していません。」
object val3 = row[col3]; // ArgumentNullException「'column' 引数を Null にすることはできません。」

object val4 = row.Field<object>(col1); // null
object val5 = row.Field<object>(col2); // ArgumentException「列 'col2' はテーブル table に属していません。」
object val6 = row.Field<object>(col3); // ArgumentNullException「'column' 引数を Null にすることはできません。」

SetField<T>()

指定の列に、新しい値を設定できます。

public static void SetField<T> (
    this System.Data.DataRow row,
    string columnName,
    T value
    );
SetField<T>(DataRow, String, T) - DataRowExtensions.SetField Method (System.Data) | Microsoft Learn
DataTable table = new DataTable();
table.Columns.Add("col1", typeof(object));
table.Columns.Add("col2", typeof(int));

DataRow row = table.NewRow();

// col1
row.SetField("col1", DBNull.Value); // OK 
row.SetField<object>("col1", null); // OK
row.SetField<int>("col1", 1); // OK

row["col1"] = DBNull.Value; // OK
row["col1"] = null;         // OK

// col2
row.SetField("col2", DBNull.Value); // OK
row.SetField<object>("col2", null); // OK
row.SetField<string>("col2", "a"); // FormatException「入力文字列の形式が正しくありません。」

row["col2"] = DBNull.Value; // OK
row["col2"] = null;         // ArgumentException「Column 'col2' を null に設定できません。DBNull を使用してください。」

このメソッドは次のように実装されています。

public static void SetField<T>(this DataRow row, string columnName, T value) {
    DataSetUtil.CheckArgumentNull(row, "row");
    row[columnName] = (object)value ?? DBNull.Value;
}
SetField - DataRowExtensions.cs

そのためnullを指定するとDBNull.Valueが設定されます。

DataTable table = new DataTable();
DataColumn col1 = table.Columns.Add("", typeof(object));

DataRow row = table.NewRow();

row.SetField(col1, DBNull.Value);
object v1 = row.Field<object>(col1); // null
object v2 = row[col1];               // DBNull

row.SetField<object>(col1, null);
object v3 = row.Field<object>(col1); // null
object v4 = row[col1];               // DBNull

GetChanges()

DataRowにはGetChanges()が実装されていないため、変更されたデータは独自に取得する必要があります。.net - DataRow.GetChanges() or equivalent - Stack Overflow

// 変更されたデータを含む、DataTableのコピーを取得する
DataTable changedTable = dataTable.GetChanges();

// 変更されたデータを含む行を、1行ずつ確認する
foreach (DataRow row in changedTable.Rows)
{
    // すべての列のデータを、1つずつ確認する
    foreach (DataColumn column in changedTable.Columns)
    {
        object original = row[column, DataRowVersion.Original];
        object current = row[column, DataRowVersion.Current];

        if (!object.Equals(original, current))
        {
            // row[column]が変更されている
        }
    }
}
Microsoft Learnから検索