ID 値および Autonumber 値の取得 (ADO.NET)

リレーショナル データベースの主キーとは、常に一意の値を含んだ列または列の組み合わせをいいます。 主キー値がわかっていれば、それが格納されている行を特定できます。 SQL Server、Oracle、Microsoft Access/Jet などのリレーショナル データベース エンジンは、主キーとして指定可能な自動インクリメント列の作成をサポートしています。 これらの値はテーブルに行を追加するとサーバーによって自動的に生成されます。 SQL Server では列の Identity プロパティを設定し、Oracle では Sequence を作成します。また、Microsoft Access では、AutoNumber 列を作成します。

DataColumn を使用し、AutoIncrement プロパティを true に設定することで、インクリメント値を自動的に生成することもできます。 ただし、複数のクライアント アプリケーションがそれぞれ独立して自動インクリメント値を生成した場合、最終的に DataTable の別々のインスタンスに重複する値が存在することも考えられます。 サーバーで自動的にインクリメント値を生成すると、各ユーザーが、挿入された行ごとに生成された値を取得できるようになるため、競合を未然に防ぐことができます。

ADO.NET アプリケーションでは、DataAdapter の Update メソッドを呼び出している間、データベースから出力パラメーターとして、または (INSERT ステートメントと同じバッチで実行した) SELECT ステートメントの結果セットの最初に返されたレコードとして、データを取得できます。 これらの値を取得することで、更新対象となる DataRow 内の対応する列を更新できます。

Microsoft Access Jet データベース エンジンなど、一部のデータベース エンジンは、出力パラメーターをサポートしておらず、複数のステートメントを 1 回のバッチで処理することもできません。 Jet データベース エンジンを使用する場合は、DataAdapter の RowUpdated イベントのイベント ハンドラーで別途 SELECT コマンドを実行することによって、挿入行に対して生成された新しい AutoNumber 値を取得できます。

メモメモ

自動インクリメント値を使用する代わりに、クライアント コンピューター側で Guid オブジェクトの NewGuid メソッドを使用して GUID (グローバルな一意識別子) を生成し、新しい行が挿入されるたびにそれをサーバーにコピーする方法もあります。NewGuid メソッドでは、値の重複を高い確率で防ぐアルゴリズムを使って 16 バイトのバイナリ値が生成されます。SQL Server データベースでは、Transact-SQL の NEWID() 関数を使って自動的に生成される GUID が、uniqueidentifier 列に格納されます。GUID を主キーとして使用すると、パフォーマンスが低下する場合があります。SQL Server 2005 では、シーケンシャルな GUID を生成する NEWSEQUENTIALID() 関数が新たにサポートされています。グローバルな一意性は保証されませんが、より効率的なインデックス処理が可能となります。

SQL Server の ID 列値の取得

Microsoft SQL Server を使用している場合は、出力パラメーターを持ったストアド プロシージャを作成して、挿入された行の ID 値を取得できます。 次の表は、SQL Server で ID 列値の取得に使用できる 3 つの Transact-SQL 関数を示しています。

関数

説明

SCOPE_IDENTITY

現在の実行スコープ内の最後の ID 値を返します。 通常は、SCOPE_IDENTITY を使用することをお勧めします。

@@IDENTITY

現在のセッション内の任意のテーブルで生成された最後の ID 値を保持します。 @@IDENTITY はトリガーの影響を受けるため、期待した ID 値が返されない場合があります。

IDENT_CURRENT

任意のセッションとスコープ内の特定のテーブルに対して生成された最後の ID 値を返します。

次のストアド プロシージャは、Categories テーブルに行を挿入し、Transact-SQL の SCOPE_IDENTITY() 関数によって生成された新しい ID 値を出力パラメーターとして取得する方法を示しています。

CREATE PROCEDURE dbo.InsertCategory
  @CategoryName nvarchar(15),
  @Identity int OUT
AS
INSERT INTO Categories (CategoryName) VALUES(@CategoryName)
SET @Identity = SCOPE_IDENTITY()

このストアド プロシージャは SqlDataAdapter オブジェクトの InsertCommand のソースとして指定できます。 InsertCommandCommandType プロパティには、有効な StoredProcedure を設定する必要があります。 ID の出力を取得するには、ParameterDirectionOutput に設定した SqlParameter を作成します。 挿入コマンドの UpdatedRowSource プロパティを UpdateRowSource.OutputParameters または UpdateRowSource.Both に設定した場合、InsertCommand が処理されると、自動的にインクリメントされた ID 値が返され、現在の行の CategoryID 列に設定されます。

挿入コマンドで、INSERT ステートメントと (新しい ID 値を返す) SELECT ステートメントの両方を含むバッチを実行した場合、挿入コマンドの UpdatedRowSource プロパティを UpdateRowSource.FirstReturnedRecord に設定することによって新しい値を取得できます。

Private Sub RetrieveIdentity(ByVal connectionString As String)
    Using connection As SqlConnection = New SqlConnection( _
       connectionString)

        ' Create a SqlDataAdapter based on a SELECT query.
        Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
           "SELECT CategoryID, CategoryName FROM dbo.Categories", _
           connection)

        ' Create the SqlCommand to execute the stored procedure. 
        adapter.InsertCommand = New SqlCommand("dbo.InsertCategory", _
           connection)
        adapter.InsertCommand.CommandType = CommandType.StoredProcedure

        ' Add the parameter for the CategoryName. Specifying the
        ' ParameterDirection for an input parameter is not required.
        adapter.InsertCommand.Parameters.Add( _
          "@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

        ' Add the SqlParameter to retrieve the new identity value.
        ' Specify the ParameterDirection as Output.
        Dim parameter As SqlParameter = _
           adapter.InsertCommand.Parameters.Add( _
          "@Identity", SqlDbType.Int, 0, "CategoryID")
        parameter.Direction = ParameterDirection.Output

        ' Create a DataTable and fill it.
        Dim categories As DataTable = New DataTable
        adapter.Fill(categories)

        ' Add a new row.
        Dim newRow As DataRow = categories.NewRow()
        newRow("CategoryName") = "New Category"
        categories.Rows.Add(newRow)

        ' Update the database.
        adapter.Update(categories)

        Console.WriteLine("List All Rows:")
        Dim row As DataRow
        For Each row In categories.Rows
            Console.WriteLine("{0}: {1}", row(0), row(1))
        Next
    End Using
End Sub
private static void RetrieveIdentity(string connectionString)
{
    using (SqlConnection connection =
               new SqlConnection(connectionString))
    {
        // Create a SqlDataAdapter based on a SELECT query.
        SqlDataAdapter adapter =
            new SqlDataAdapter(
            "SELECT CategoryID, CategoryName FROM dbo.Categories",
            connection);

        //Create the SqlCommand to execute the stored procedure.
        adapter.InsertCommand = new SqlCommand("dbo.InsertCategory", 
            connection);
        adapter.InsertCommand.CommandType = CommandType.StoredProcedure;

        // Add the parameter for the CategoryName. Specifying the
        // ParameterDirection for an input parameter is not required.
        adapter.InsertCommand.Parameters.Add(
           new SqlParameter("@CategoryName", SqlDbType.NVarChar, 15,
           "CategoryName"));

        // Add the SqlParameter to retrieve the new identity value.
        // Specify the ParameterDirection as Output.
        SqlParameter parameter = 
            adapter.InsertCommand.Parameters.Add(
            "@Identity", SqlDbType.Int, 0, "CategoryID");
        parameter.Direction = ParameterDirection.Output;

        // Create a DataTable and fill it.
        DataTable categories = new DataTable();
        adapter.Fill(categories);

        // Add a new row. 
        DataRow newRow = categories.NewRow();
        newRow["CategoryName"] = "New Category";
        categories.Rows.Add(newRow);

        adapter.Update(categories);

        Console.WriteLine("List All Rows:");
        foreach (DataRow row in categories.Rows)
        {
            {
                Console.WriteLine("{0}: {1}", row[0], row[1]);
            }
        }
    }
}

新しい ID 値の結合

DataTable の GetChanges は、変更行だけを含んだコピーを作成するメソッドです。作成した新しいコピーは DataAdapter の Update メソッドの呼び出し時に使用できます。 この方法を用いると、変更された行を別のコンポーネントにマーシャリングして更新を実行できます。 更新後、このコピーには、元の DataTable に再び結合する必要のある新しい ID 値が格納されている可能性もあります。 新しい ID 値は、おそらく DataTable 内の元の値とは異なります。 結合を実行するには、新しい ID 値を含んだ新しい行を追加するのではなく、コピー内の AutoIncrement 列の元の値を保持しておき、元の DataTable から既存の行を特定して更新する必要があります。 しかし、既定では、DataAdapter の Update メソッドを呼び出した後、元の値は失われてしまいます。更新された DataRow ごとに AcceptChanges が暗黙的に呼び出されるためです。

DataAdapter の更新処理中、DataRow の DataColumn に格納されていた元の値を保持するには、次の 2 とおりの方法があります。

  • 元の値を保持する 1 つ目の方法は、DataAdapter の AcceptChangesDuringUpdate プロパティを false に設定することです。 この設定は、更新対象となる DataTable のすべての DataRow に影響します。 詳細とコード例については、「AcceptChangesDuringUpdate」を参照してください。

  • 2 つ目は、DataAdapter の RowUpdated イベント ハンドラーに、StatusSkipCurrentRow に設定するコードを記述する方法です。 DataRow が更新されても、各 DataColumn の元の値は保持されます。 この方法を使用した場合、一部の行についてのみ元の値を保持するといったことも可能です。 たとえば、最初に StatementType を調べ、StatementType が Insert である行に限定して StatusSkipCurrentRow に設定することで、追加された行については元の値を保持し、編集または削除された行については元の値を保持しないようなコードを作成できます。

DataAdapter の更新処理中、上記のいずれかの方法で DataRow の元の値を保持した場合、ADO.NET は、各 DataColumn の元の値を保持する一方で、DataRow の現在の値を新しい値 (つまり、出力パラメーターまたは結果セットの先頭行として返された値) に設定するための一連の処理を実行します。 最初に DataRow の AcceptChanges メソッドが呼び出され、現在の値が元の値として保持された後、新しい値が割り当てられます。 これらの処理に続けて、RowState プロパティが Added に設定されていた DataRows については、RowState プロパティが Modified に設定されます。これはユーザーが想定していない可能性があります。

更新対象の各 DataRow に対し、コマンドの結果がどのように適用されるかは、各 DbCommandUpdatedRowSource プロパティによって決まります。 このプロパティは、UpdateRowSource 列挙型のいずれかの値に設定されます。

次の表では、UpdateRowSource 列挙型の各値が、更新対象の行の RowState プロパティにどのように影響するかを説明します。

メンバー名

説明

Both

AcceptChanges が呼び出され、出力パラメーターの値と、返された結果セットの先頭行が、どちらも更新対象の DataRow に格納されます。 適用する値が存在しない場合、RowState は Unchanged になります。

FirstReturnedRecord

行が返された場合、AcceptChanges が呼び出され、その行が DataTable の変更行にマップされて、RowState が Modified に設定されます。 行が返されなかった場合、AcceptChanges は呼び出されず、RowState は Added のままになります。

None

返されたパラメーターまたは行はすべて無視されます。 AcceptChanges は呼び出されず、RowState は Added のままになります。

OutputParameters

AcceptChanges が呼び出され、出力パラメーターがある場合は DataTable の変更行にマップされて、RowState が Modified に設定されます。 出力パラメーターがない場合は、RowState は Unchanged になります。

次の例では、DataTable から変更行を抽出し、SqlDataAdapter でデータ ソースを更新して、新しい ID 列値を取得します。 InsertCommand は、2 つの Transact-SQL ステートメントを実行します。1 つは INSERT ステートメントで、もう 1 つは、SCOPE_IDENTITY 関数を使って ID 値を取得する SELECT ステートメントです。

INSERT INTO dbo.Shippers (CompanyName) 
VALUES (@CompanyName);
SELECT ShipperID, CompanyName FROM dbo.Shippers 
WHERE ShipperID = SCOPE_IDENTITY();

挿入コマンドの UpdatedRowSource プロパティは UpdateRowSource.FirstReturnedRow に、DataAdapter の MissingSchemaAction プロパティは MissingSchemaAction.AddWithKey に設定します。 DataTable にデータを格納した後、新しい行を DataTable に追加します。 次に、変更行を抽出して新しい DataTable に格納します。このデータ テーブルが DataAdapter に渡され、サーバー側が更新されます。

Private Sub MergeIdentityColumns(ByVal connectionString As String)

    Using connection As SqlConnection = New SqlConnection( _
       connectionString)

        ' Create the DataAdapter
        Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
          "SELECT ShipperID, CompanyName FROM dbo.Shippers", connection)

        ' Add the InsertCommand to retrieve new identity value.
        adapter.InsertCommand = New SqlCommand( _
            "INSERT INTO dbo.Shippers (CompanyName) " & _
            "VALUES (@CompanyName); " & _
            "SELECT ShipperID, CompanyName FROM dbo.Shippers " & _
            "WHERE ShipperID = SCOPE_IDENTITY();", _
            connection)

        ' Add the parameter for the inserted value.
        adapter.InsertCommand.Parameters.Add( _
           New SqlParameter("@CompanyName", SqlDbType.NVarChar, 40, _
           "CompanyName"))
        adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both

        ' MissingSchemaAction adds any missing schema to 
        ' the DataTable, including identity columns
        adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey

        ' Fill the DataTable.
        Dim shipper As New DataTable
        adapter.Fill(shipper)

        ' Add a new shipper. 
        Dim newRow As DataRow = shipper.NewRow()
        newRow("CompanyName") = "New Shipper"
        shipper.Rows.Add(newRow)

        ' Add changed rows to a new DataTable. This
        ' DataTable will be used by the DataAdapter.
        Dim dataChanges As DataTable = shipper.GetChanges()

        ' Add the event handler. 
        AddHandler adapter.RowUpdated, New _
           SqlRowUpdatedEventHandler(AddressOf OnRowUpdated)

        ' Update the datasource with the modified records.
        adapter.Update(dataChanges)

        ' Merge the two DataTables.
        shipper.Merge(dataChanges)

        ' Commit the changes.
        shipper.AcceptChanges()

        Console.WriteLine("Rows after merge.")
        Dim row As DataRow
        For Each row In shipper.Rows
            Console.WriteLine("{0}: {1}", row(0), row(1))
        Next
    End Using
End Sub
private static void MergeIdentityColumns(string connectionString)
{
    using (SqlConnection connection =
               new SqlConnection(connectionString))
    {
        // Create the DataAdapter
        SqlDataAdapter adapter =
            new SqlDataAdapter(
            "SELECT ShipperID, CompanyName FROM dbo.Shippers",
            connection);

        //Add the InsertCommand to retrieve new identity value.
        adapter.InsertCommand = new SqlCommand(
            "INSERT INTO dbo.Shippers (CompanyName) " +
            "VALUES (@CompanyName); " +
            "SELECT ShipperID, CompanyName FROM dbo.Shippers " +
            "WHERE ShipperID = SCOPE_IDENTITY();", connection);

        // Add the parameter for the inserted value.
        adapter.InsertCommand.Parameters.Add(
           new SqlParameter("@CompanyName", SqlDbType.NVarChar, 40,
           "CompanyName"));
        adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both;

        // MissingSchemaAction adds any missing schema to 
        // the DataTable, including identity columns
        adapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;

        // Fill the DataTable.
        DataTable shipper = new DataTable();
        adapter.Fill(shipper);

        // Add a new shipper. 
        DataRow newRow = shipper.NewRow();
        newRow["CompanyName"] = "New Shipper";
        shipper.Rows.Add(newRow);

        // Add changed rows to a new DataTable. This
        // DataTable will be used by the DataAdapter.
        DataTable dataChanges = shipper.GetChanges();

        // Add the event handler. 
        adapter.RowUpdated +=
            new SqlRowUpdatedEventHandler(OnRowUpdated);

        adapter.Update(dataChanges);
        connection.Close();

        // Merge the updates.
        shipper.Merge(dataChanges);

        // Commit the changes.
        shipper.AcceptChanges();

        Console.WriteLine("Rows after merge.");
        foreach (DataRow row in shipper.Rows)
        {
            {
                Console.WriteLine("{0}: {1}", row[0], row[1]);
            }
        }
    }
}

OnRowUpdated イベント ハンドラーでは、SqlRowUpdatedEventArgsStatementType をチェックして、行が挿入であるかどうかを調べています。 挿入である場合は、Status プロパティを SkipCurrentRow に設定します。 行は更新されますが、その行の元の値は保持されます。 プロシージャの本体では、Merge メソッドを呼び出すことによって、新しい ID 値を元の DataTable に結合し、最後に AcceptChanges を呼び出します。

Private Sub OnRowUpdated( _
    ByVal sender As Object, ByVal e As SqlRowUpdatedEventArgs)
    ' If this is an insert, then skip this row.
    If e.StatementType = StatementType.Insert Then
        e.Status = UpdateStatus.SkipCurrentRow
    End If
End Sub
protected static void OnRowUpdated(
    object sender, SqlRowUpdatedEventArgs e)
{
    // If this is an insert, then skip this row.
    if (e.StatementType == StatementType.Insert)
    {
        e.Status = UpdateStatus.SkipCurrentRow;
    }
}

Microsoft Access の Autonumber 値の取得

このセクションでは、Jet 4.0 データベースから Autonumber 値を取得する方法を例で示します。 Jet データベース エンジンは、複数のステートメントをバッチとして実行することも、出力パラメーターを使用することもできません。そのため、前述した方法では、挿入行に割り当てられた新しい Autonumber 値を取得することはできません。 しかし、RowUpdated イベント ハンドラーにコードを追加し、別途 SELECT @@IDENTITY ステートメントを実行することによって、新しい Autonumber 値を取得できます。

この例では、MissingSchemaAction.AddWithKey を使ってスキーマ情報を追加する代わりに、あらかじめ適切なスキーマで DataTable を構成した後、OleDbDataAdapter を呼び出して DataTable にデータを格納しています。 CategoryID 列は、0 を開始番号とし、行が挿入されるたびに値が減少するように構成するため、AutoIncrement を true に、AutoIncrementSeed を 0 に、AutoIncrementStep を -1 に設定しています。 その後、2 つの新しい行を追加し、GetChanges を使って変更行を新しい DataTable に追加します。Update メソッドには、このデータ テーブルが渡されます。

Shared connection As OleDbConnection = Nothing

Private Shared Sub MergeIdentityColumns(ByVal connection As OleDbConnection)
    Using connection

        ' Create a DataAdapter based on a SELECT query.
        Dim adapter As OleDbDataAdapter = New OleDbDataAdapter( _
          "SELECT CategoryID, CategoryName FROM Categories", _
          connection)

        ' Create the INSERT command for the new category.
        adapter.InsertCommand = New OleDbCommand( _
          "INSERT INTO Categories (CategoryName) Values(?)", connection)
        adapter.InsertCommand.CommandType = CommandType.Text

        ' Add the parameter for the CategoryName.
        adapter.InsertCommand.Parameters.Add( _
          "@CategoryName", OleDbType.VarWChar, 15, "CategoryName")
        adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both

        ' Create a DataTable.
        Dim categories As DataTable = New DataTable

        ' Create the CategoryID column and set its auto
        ' incrementing properties to decrement from zero.
        Dim column As New DataColumn()
        column.DataType = System.Type.GetType("System.Int32")
        column.ColumnName = "CategoryID"
        column.AutoIncrement = True
        column.AutoIncrementSeed = 0
        column.AutoIncrementStep = -1
        categories.Columns.Add(column)

        ' Create the CategoryName column.
        column = New DataColumn()
        column.DataType = System.Type.GetType("System.String")
        column.ColumnName = "CategoryName"
        categories.Columns.Add(column)

        ' Set the primary key on CategoryID.
        Dim pKey(1) As DataColumn
        pKey(0) = categories.Columns("CategoryID")
        categories.PrimaryKey = pKey

        ' Fetch the data and fill the DataTable.
        adapter.Fill(categories)

        ' Add a new row.
        Dim newRow As DataRow = categories.NewRow()
        newRow("CategoryName") = "New Category"
        categories.Rows.Add(newRow)

        ' Add another new row.
        Dim newRow2 As DataRow = categories.NewRow()
        newRow2("CategoryName") = "Another New Category"
        categories.Rows.Add(newRow2)

        ' Add changed rows to a new DataTable that will be
        ' used to post the inserts to the database.
        Dim dataChanges As DataTable = categories.GetChanges()

        ' Include an event to fill in the Autonumber value.
        AddHandler adapter.RowUpdated, _
          New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)

        ' Update the database, inserting the new rows.
        adapter.Update(dataChanges)

        Console.WriteLine("Rows before merge:")
        Dim row1 As DataRow
        For Each row1 In categories.Rows
            Console.WriteLine("  {0}: {1}", row1(0), row1(1))
        Next

        ' Merge the two DataTables.
        categories.Merge(dataChanges)

        ' Commit the changes.
        categories.AcceptChanges()

        Console.WriteLine("Rows after merge:")
        Dim row As DataRow
        For Each row In categories.Rows
            Console.WriteLine("  {0}: {1}", row(0), row(1))
        Next
    End Using
End Sub
private static OleDbConnection connection = null;

private static void MergeIdentityColumns(OleDbConnection connection)
{
    using (connection)
    {
        // Create a DataAdapter based on a SELECT query.
        OleDbDataAdapter adapter = new OleDbDataAdapter(
         "SELECT CategoryID, CategoryName FROM Categories",
         connection);

        // Create the INSERT command for the new category.
        adapter.InsertCommand = new OleDbCommand(
          "INSERT INTO Categories (CategoryName) Values(?)", connection);
        adapter.InsertCommand.CommandType = CommandType.Text;

        // Add the parameter for the CategoryName.
        adapter.InsertCommand.Parameters.Add(
          "@CategoryName", OleDbType.VarWChar, 15, "CategoryName");
        adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.Both;

        // Create a DataTable
        DataTable categories = new DataTable();

        // Create the CategoryID column and set its auto 
        // incrementing properties to decrement from zero. 
        DataColumn column = new DataColumn();
        column.DataType = System.Type.GetType("System.Int32");
        column.ColumnName = "CategoryID";
        column.AutoIncrement = true;
        column.AutoIncrementSeed = 0;
        column.AutoIncrementStep = -1;
        categories.Columns.Add(column);

        // Create the CategoryName column.
        column = new DataColumn();
        column.DataType = System.Type.GetType("System.String");
        column.ColumnName = "CategoryName";
        categories.Columns.Add(column);

        // Set the primary key on CategoryID.
        DataColumn[] pKey = new DataColumn[1];
        pKey[0] = categories.Columns["CategoryID"];
        categories.PrimaryKey = pKey;

        // Fetch the data and fill the DataTable
        adapter.Fill(categories);

        // Add a new row.
        DataRow newRow = categories.NewRow();
        newRow["CategoryName"] = "New Category";
        categories.Rows.Add(newRow);

        // Add another new row.
        DataRow newRow2 = categories.NewRow();
        newRow2["CategoryName"] = "Another New Category";
        categories.Rows.Add(newRow2);

        // Add changed rows to a new DataTable that will be
        // used to post the inserts to the database.
        DataTable dataChanges = categories.GetChanges();

        // Include an event to fill in the Autonumber value.
        adapter.RowUpdated +=
            new OleDbRowUpdatedEventHandler(OnRowUpdated);

        // Update the database, inserting the new rows. 
        adapter.Update(dataChanges);

        Console.WriteLine("Rows before merge:");
        foreach (DataRow row in categories.Rows)
        {
            {
                Console.WriteLine("  {0}: {1}", row[0], row[1]);
            }
        }

        // Merge the two DataTables.
        categories.Merge(dataChanges);

        // Commit the changes.
        categories.AcceptChanges();

        Console.WriteLine("Rows after merge:");
        foreach (DataRow row in categories.Rows)
        {
            {
                Console.WriteLine("  {0}: {1}", row[0], row[1]);
            }
        }
    }
}

RowUpdated イベント ハンドラーでは、OleDbDataAdapter の Update ステートメントと同じ、開いている OleDbConnection を使用します。 このハンドラーでは、挿入された行について、OleDbRowUpdatedEventArgs の StatementType をチェックします。 挿入された行ごとに、新しい OleDbCommand を作成して、接続上で SELECT @@IDENTITY ステートメントを実行し、新しい Autonumber 値を取得します。取得した値は、DataRow の CategoryID 列に格納されます。 次に、AcceptChanges の暗黙的な呼び出しを抑制するため、Status プロパティを UpdateStatus.SkipCurrentRow に設定しています。 プロシージャの本体では、Merge メソッドを呼び出すことによって、2 つの DataTable を結合し、最後に AcceptChanges を呼び出します。

Private Shared Sub OnRowUpdated( _
    ByVal sender As Object, ByVal e As OleDbRowUpdatedEventArgs)
    ' Conditionally execute this code block on inserts only.
    If e.StatementType = StatementType.Insert Then
        ' Retrieve the Autonumber and store it in the CategoryID column.
        Dim cmdNewID As New OleDbCommand("SELECT @@IDENTITY", _
           connection)
        e.Row("CategoryID") = CInt(cmdNewID.ExecuteScalar)
        e.Status = UpdateStatus.SkipCurrentRow
    End If
End Sub
private static void OnRowUpdated(
  object sender, OleDbRowUpdatedEventArgs e)
{
    // Conditionally execute this code block on inserts only.
    if (e.StatementType == StatementType.Insert)
    {
        OleDbCommand cmdNewID = new OleDbCommand("SELECT @@IDENTITY",
            connection);
        // Retrieve the Autonumber and store it in the CategoryID column.
        e.Row["CategoryID"] = (int)cmdNewID.ExecuteScalar();
        e.Status = UpdateStatus.SkipCurrentRow;
    }
}

参照

概念

行の状態とバージョン

AcceptChanges と RejectChanges

DataSet の内容のマージ (ADO.NET)

DataAdapter によるデータ ソースの更新 (ADO.NET)

その他の技術情報

ADO.NET でのデータの取得および変更

DataAdapter と DataReader (ADO.NET)