Abrufen von Identitäts- oder AutoWert-Werten (ADO.NET)

Aktualisiert: November 2007

Ein Primärschlüssel in einer relationalen Datenbank ist eine Spalte oder eine Kombination aus Spalten, die stets eindeutige Werte enthält. Wenn Sie den Primärschlüsselwert kennen, können Sie die Zeile lokalisieren, die den Wert enthält. Relationale Datenbankmodule, wie SQL Server, Oracle und Microsoft Access/Jet, unterstützen das Erstellen automatisch inkrementierender Spalten, die als Primärschlüssel verwendet werden können. Diese Werte werden vom Server generiert, wenn einer Tabelle Zeilen hinzugefügt werden. In SQL Server legen Sie die Identität einer Spalte fest, in Oracle erstellen Sie eine Sequenz, und in Microsoft Access erstellen Sie eine AutoWert-Spalte.

Eine DataColumn kann durch Festlegen der AutoIncrement-Eigenschaft auf true auch zum Generieren automatisch inkrementierender Werte verwendet werden. Wenn aber mehrere Clientanwendungen unabhängig voneinander automatisch inkrementierende Werte generieren, kann es passieren, dass zum Schluss in getrennten Instanzen einer DataTable doppelte Werte vorhanden sind. Wenn der Server angewiesen wird, automatisch inkrementierende Werte zu generieren, entfallen potenzielle Konflikte, weil jeder Benutzer für jede eingefügte Zeile den generierten Wert abrufen kann.

Bei einem Aufruf der Update-Methode eines DataAdapter kann die Datenbank Daten zurück an Ihre ADO.NET-Anwendung senden, und zwar entweder als Ausgabeparameter oder als ersten zurückgegebenen Datensatz des Resultsets einer SELECT-Anweisung, die im selben Batch wie die INSERT-Anweisung ausgeführt wird. ADO.NET kann diese Werte abrufen und die entsprechenden Spalten in der zu aktualisierenden DataRow aktualisieren.

Einige Datenbankmodule, z. B. das Microsoft Access Jet-Datenbankmodul, bieten keine Unterstützung für Ausgabeparameter und sind nicht in der Lage, mehrere Anweisungen in einem einzelnen Batch zu verarbeiten. Wenn Sie mit dem Jet-Datenbankmodul arbeiten, können Sie den neuen, für eine eingefügte Zeile generierten AutoWert-Wert abrufen, indem Sie in einem Ereignishandler für das RowUpdated-Ereignis des DataAdapter einen separaten SELECT-Befehl ausführen.

Hinweis:

Statt automatisch inkrementierende Werte zu verwenden, können Sie auch mit der NewGuid-Methode eines Guid-Objekts eine GUID auf dem Clientcomputer generieren, die jedes Mal, wenn eine neue Zeile eingefügt wird, auf den Server kopiert werden kann. Die NewGuid-Methode generiert einen 16-Byte-Binärwert, der mit einem Algorithmus erstellt wird, der dafür sorgt, dass mit hoher Wahrscheinlichkeit kein Wert doppelt vorhanden ist. In einer SQL Server-Datenbank werden GUIDs in einer uniqueidentifier-Spalte gespeichert, die SQL Server automatisch mit der Transact-SQL-NEWID()-Funktion generieren kann. Die Verwendung einer GUID als Primärschlüssel kann zu Leistungseinbußen führen. Seit SQL Server 2005 wird die NEWSEQUENTIALID()-Funktion unterstützt, die eine sequenzielle GUID generiert, für die zwar keine globale Eindeutigkeit gewährleistet ist, die aber effizienter indiziert werden kann.

Abrufen von SQL Server-Identitätsspaltenwerten

Wenn Sie mit Microsoft SQL Server arbeiten, können Sie eine gespeicherte Prozedur mit einem Ausgabeparameter erstellen, um den Identitätswert für eine eingefügte Zeile zu erhalten. In der folgenden Tabelle werden die drei Transact-SQL-Funktionen in SQL Server beschrieben, mit denen Werte aus Identitätsspalten abgerufen werden können.

Funktion

Beschreibung

SCOPE_IDENTITY

Gibt den letzten Identitätswert innerhalb des aktuellen Ausführungsbereichs zurück. SCOPE_IDENTITY empfiehlt sich für die meisten Szenarios.

@@IDENTITY

Enthält den letzten Identitätswert, der in einer der Tabellen in der aktuellen Sitzung generiert wurde. @@IDENTITY kann von Triggern beeinflusst werden und gibt möglicherweise nicht den erwarteten Identitätswert zurück.

IDENT_CURRENT

Gibt den letzten Identitätswert zurück, der für eine bestimmte Tabelle in einer der Sitzungen und in einem der Bereiche generiert wurde.

Die folgende gespeicherte Prozedur zeigt, wie Sie eine Zeile in die Categories-Tabelle einfügen und einen Ausgabeparameter verwenden können, damit der von der Transact-SQL-SCOPE_IDENTITY()-Funktion generierte neue Identitätswert zurückgegeben wird.

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

Die gespeicherte Prozedur kann dann als Quelle des InsertCommand eines SqlDataAdapter-Objekts angegeben werden. Die CommandType-Eigenschaft des InsertCommand muss auf StoredProcedure festgelegt werden. Die Identitätsausgabe wird abgerufen, indem ein SqlParameter mit dem ParameterDirection-Wert Output erstellt wird. Bei der Verarbeitung des InsertCommand wird der automatisch inkrementierende Identitätswert zurückgegeben und in der CategoryID-Spalte der aktuellen Zeile platziert, sofern Sie die UpdatedRowSource-Eigenschaft des Einfügebefehls auf UpdateRowSource.OutputParameters oder auf UpdateRowSource.Both festgelegt haben.

Wenn Ihr Einfügebefehl einen Batch ausführt, der sowohl eine INSERT-Anweisung als auch eine SELECT-Anweisung enthält, die den neuen Identitätswert zurückgibt, können Sie den neuen Wert abrufen, indem Sie für die UpdatedRowSource-Eigenschaft des Einfügebefehls den Wert UpdateRowSource.FirstReturnedRecord festlegen.

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]);
            }
        }
    }
}

Zusammenführen neuer Identitätswerte

Häufig wird die GetChanges-Methode einer DataTable verwendet, um eine Kopie zu erstellen, die nur geänderte Zeilen enthält. Beim Aufrufen der Update-Methode eines DataAdapter kommt dann die neue Kopie zum Einsatz. Diese Vorgehensweise ist vor allem dann sinnvoll, wenn Sie die geänderten Zeilen in eine separate Komponente marshallen müssen, die die Aktualisierung ausführt. Im Anschluss an die Aktualisierung kann die Kopie die neuen Identitätswerte enthalten, die dann in der ursprünglichen DataTable wieder zusammengeführt werden müssen. Die neuen Identitätswerte weichen wahrscheinlich von den ursprünglichen Werten in der DataTable ab. Um die Zusammenführung abschließen zu können, müssen die ursprünglichen Werte der AutoIncrement-Spalten in der Kopie beibehalten werden, weil nur so die vorhandenen Zeilen in der ursprünglichen DataTable lokalisiert und aktualisiert werden können. Anderenfalls würden die neuen Zeilen mit den neuen Identitätswerten angehängt werden. Standardmäßig gehen diese ursprünglichen Werte aber nach einem Aufruf der Update-Methode eines DataAdapter verloren, weil für jede aktualisierte DataRow implizit AcceptChanges aufgerufen wird.

Zum Beibehalten der ursprünglichen Werte einer DataColumn in einer DataRow während einer DataAdapter-Aktualisierung gibt es die folgenden beiden Möglichkeiten:

  • Die erste Methode besteht darin, für die AcceptChangesDuringUpdate-Eigenschaft des DataAdapterfalse festzulegen. Dies wirkt sich auf jede DataRow in der zu aktualisierenden DataTable aus. Weitere Informationen dazu sowie ein Codebeispiel finden Sie unter AcceptChangesDuringUpdate.

  • Die zweite Methode besteht darin, Code im RowUpdated-Ereignishandler des DataAdapter zu schreiben und damit den Status auf SkipCurrentRow zu setzen. Die DataRow wird zwar aktualisiert, aber die einzelnen ursprünglichen DataColumn-Werte bleiben erhalten. Diese Methode ermöglicht es Ihnen, die ursprünglichen Werte für einige Zeilen beizubehalten und für andere nicht. Ihr Code kann z. B. die ursprünglichen Werte für hinzugefügte Zeilen beibehalten, während die ursprünglichen Werte für bearbeitete oder gelöschte Zeilen verloren gehen, indem er zunächst den StatementType prüft und dann den Status nur für die Zeilen mit dem StatementTypeInsert auf SkipCurrentRow setzt.

Wenn bei einer DataAdapter-Aktualisierung eine dieser beiden Methoden zur Beibehaltung von ursprünglichen Werten in einer DataRow verwendet wird, führt ADO.NET eine Reihe von Aktionen aus, um die aktuellen DataRow-Werte auf die neuen Werte zu setzen, die von Ausgabeparametern oder der ersten zurückgegebenen Zeile eines Resultsets zurückgegeben werden. Die einzelnen ursprünglichen DataColumn-Werte bleiben dabei erhalten. Als Erstes wird die AcceptChanges-Methode der DataRow aufgerufen, um die aktuellen Werte als ursprüngliche Werte beizubehalten. Anschließend werden die neuen Werte zugewiesen. Als Nächstes wird bei DataRows, deren RowState-Eigenschaft den Wert Added hat, der RowState-Wert in Modified geändert, was u. U. nicht den Erwartungen entspricht.

Wie die Befehlsergebnisse auf jede zu aktualisierende DataRow angewendet werden, richtet sich nach der UpdatedRowSource-Eigenschaft des jeweiligen DbCommand. Diese Eigenschaft wird auf einen Wert aus der UpdateRowSource-Enumeration festgelegt.

Die folgende Tabelle zeigt, wie sich die UpdateRowSource-Enumerationswerte auf die RowState-Eigenschaft aktualisierter Zeilen auswirken.

Membername

Beschreibung

Both

AcceptChanges wird aufgerufen, und die Ausgabeparameterwerte und/oder die Werte der ersten Zeile aller zurückgegebenen Resultsets werden in der zu aktualisierenden DataRow platziert. Wenn es keine zu übernehmenden Werte gibt, lautet der RowStateUnchanged.

FirstReturnedRecord

Wenn eine Zeile zurückgegeben wurde, wird AcceptChanges aufgerufen und die Zeile wird der geänderten Zeile in der DataTable zugeordnet, wobei der RowState auf Modified gesetzt wird. Wenn keine Zeile zurückgegeben wird, wird AcceptChanges nicht aufgerufen, und der RowState bleibt Added.

None

Alle zurückgegebenen Parameter oder Zeilen werden ignoriert. AcceptChanges wird nicht aufgerufen, und der RowState bleibt Added.

OutputParameters

AcceptChanges wird aufgerufen, und alle Ausgabeparameter werden der geänderten Zeile in der DataTable zugeordnet, wobei der RowState auf Modified gesetzt wird. Wenn keine Ausgabeparameter vorhanden sind, lautet der RowStateUnchanged.

Beispiel

Dieses Beispiel zeigt das Extrahieren geänderter Zeilen aus einer DataTable und das Verwenden eines SqlDataAdapter, um die Datenquelle zu aktualisieren und einen neuen Wert aus der Identitätsspalte abzurufen. Der InsertCommand führt zwei Transact-SQL-Anweisungen aus: Die erste Anweisung ist die INSERT-Anweisung, und die zweite Anweisung ist eine SELECT-Anweisung, die zum Abrufen des Identitätswerts die Funktion SCOPE_IDENTITY verwendet.

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

Die UpdatedRowSource-Eigenschaft des Einfügebefehls wird auf UpdateRowSource.FirstReturnedRow gesetzt, und die MissingSchemaAction-Eigenschaft des DataAdapter wird auf MissingSchemaAction.AddWithKey gesetzt. Die DataTable wird gefüllt, und der Code fügt der DataTable eine neue Zeile hinzu. Die geänderten Zeilen werden dann in eine neue DataTable extrahiert, die an den DataAdapter übergeben wird, der dann wiederum den Server aktualisiert.

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]);
            }
        }
    }
}

Der OnRowUpdated-Ereignishandler prüft den StatementType der SqlRowUpdatedEventArgs, um festzustellen, ob es sich bei der Zeile um eine Einfügung handelt. Wenn ja, wird die Status-Eigenschaft auf SkipCurrentRow gesetzt. Die Zeile wird aktualisiert, aber die ursprünglichen Werte in der Zeile werden beibehalten. Im Hauptteil der Prozedur wird die Merge-Methode aufgerufen, um den neuen Identitätswert in der ursprünglichen DataTable zusammenzuführen. Zum Schluss wird die AcceptChanges-Methode aufgerufen.

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;
    }
}

Abrufen von Microsoft Access-AutoWert-Werten

Dieser Abschnitt enthält ein Beispiel, das die Vorgehensweise beim Abrufen von Autonumber-Werten aus einer Jet 4.0-Datenbank zeigt. Das Jet-Datenbankmodul bietet keine Unterstützung für die Ausführung mehrerer Anweisungen in einem Batch oder für die Verwendung von Ausgabeparametern. Es ist daher nicht möglich, mit einer der beiden Möglichkeiten den neuen Autonumber-Wert zurückzugeben, der einer eingefügten Zeile zugewiesen wurde. Sie können aber dem RowUpdated-Ereignishandler Code hinzufügen, der eine separate SELECT @@IDENTITY-Anweisung zum Abrufen des neuen Autonumber-Werts ausführt.

Beispiel

Statt mit MissingSchemaAction.AddWithKey Schemainformationen hinzuzufügen, konfiguriert dieses Beispiel eine DataTable mit dem korrekten Schema, bevor der OleDbDataAdapter zum Füllen der DataTable aufgerufen wird. In diesem Fall wird die CategoryID-Spalte so konfiguriert, dass der den einzelnen eingefügten Spalten zugewiesene Wert, beginnend mit Null, abnimmt. Dazu wird AutoIncrement auf true, AutoIncrementSeed auf 0 und AutoIncrementStep auf -1 gesetzt. Der Code fügt dann zwei neue Zeilen hinzu und verwendet GetChanges, um die geänderten Zeilen einer neuen DataTable hinzuzufügen, die an die Update-Methode übergeben wird.

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]);
            }
        }
    }
}

Der RowUpdated-Ereignishandler verwendet dieselbe geöffnete OleDbConnection wie die Update-Anweisung des OleDbDataAdapter. Er prüft den StatementType der OleDbRowUpdatedEventArgs auf eingefügte Zeilen. Für jede eingefügte Zeile wird ein neuer OleDbCommand erstellt, um die SELECT @@IDENTITY-Anweisung für die Verbindung auszuführen, die den neuen Autonumber-Wert zurückgibt, der in der CategoryID-Spalte der DataRow platziert wird. Die Status-Eigenschaft wird dann auf UpdateStatus.SkipCurrentRow gesetzt, um den versteckten Aufruf von AcceptChanges zu unterdrücken. Im Hauptteil der Prozedur wird die Merge-Methode aufgerufen, um die beiden DataTable-Objekte zusammenzuführen. Zum Schluss erfolgt ein Aufruf der AcceptChanges-Methode.

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;
    }
}

Siehe auch

Konzepte

Zeilenstatus und Zeilenversion

'AcceptChanges' und 'RejectChanges'

Zusammenführen von 'DataSet'-Inhalten (ADO.NET)

Aktualisieren von Datenquellen mit 'DataAdapters' (ADO.NET)

Weitere Ressourcen

Abrufen und Ändern von Daten in ADO.NET

'DataAdapters' und 'DataReaders' (ADO.NET)