Jetzt ist das Problem geklärt:
Weil ich DataAdapter.MissingSchemaAction.AddWithKey eingestellt hatte, hat
die neue leere DataTable die Eigenschaft AutoIncrement für den PrimaryKey
bekommen. Wenn bei Fill keine Rows gefüllt werden, beginnt die erste
hinzugefügte Row bei 0 zu zählen. Wenn schon Rows gelesen wurden, wird der
nächst höhere Wert in der lokalen DataTable erzeugt. Dieser stimmt nur
zufällig mit dem nächsten Identity Wert vom SQL Server überein - wenn dort
zwischendurch nichts passiert. Die beiden Keys zählen also unabhängig
voneinander.
DataAdapter.InsertCommand liest einen neuen Identity Wert standardmäßig
nicht zurück!
Also muss das InsertCommand manipuliert werden. Das jedoch selbst schreiben
kommt nicht in Frage, wozu gibt es den CommandBuilder. Dessen
GetInsertCommand kann zwar mit den erforderlichen Änderungen versehen
werden, aber das hat keine Wirkung:
With DataAdapter
.InsertCommand = CommandBuilder.GetInsertCommand
.InsertCommand.CommandText &= ";SELECT ID FROM Tab1 WHERE ID =
SCOPE_IDENTITY()"
.InsertCommand.UpdatedRowSource = UpdateRowSource.Both
End With
Das eigene InsertCommand immer zu erzeugen obwohl es selten gebraucht wird,
soll vermieden werden. Der CommandBuilder macht das auch nur auf
Anforderung. Er lässt sich allerdings nicht überschreiben..
Wir erfahren ob ein Insert demnächst zu erwarten ist in folgendem Event:
Dim cb As New SqlClient.SqlCommandBuilder(dadp)
Dim WithEvents dt As DataTable("Tab1") ' muss dem Name vom SQL Server
entsprechen
Private Sub dt_RowChanged(ByVal sender As Object, ByVal e As
System.Data.DataRowChangeEventArgs) Handles dt.RowChanged
If e.Action = DataRowAction.Add AndAlso dadp.InsertCommand Is
Nothing Then
'
With cb.DataAdapter
Dim cbInsert As SqlClient.SqlCommand = cb.GetInsertCommand
.InsertCommand = New SqlClient.SqlCommand
.InsertCommand.Connection = cbInsert.Connection
.InsertCommand.CommandText = cbInsert.CommandText & ";SELECT
IDENTITYCOL FROM " & e.Row.Table.TableName & " WHERE IDENTITYCOL =
SCOPE_IDENTITY()"
For Each cbPar As SqlClient.SqlParameter In
cbInsert.Parameters
With .InsertCommand.Parameters.Add(New
SqlClient.SqlParameter)
.ParameterName = cbPar.ParameterName
.SourceColumn = cbPar.SourceColumn
' evtl. weitere Properties abschreiben
End With
Next
End With
'MsgBox(dadp.InsertCommand.CommandText)
End If
End Sub
Es wird also nur beim Hinzufügen einer neuen DataRow ein InsertCommand für
den DataAdapter erzeugt. Und wenn dieser einmal eins hat, lässt er vom
CommandBuilder keins mehr generieren.
Als Vorlage wird das vom CommandBuilder automatisch erzeugte InsertCommand
benutzt. Der CommandText ist schon richtig und wird nur um die SELECT
Anweisung verlängert. Diese ist allgemein und passt immer. Vorausgesetzt der
TableName entspricht dem auf dem SQL Server.
Die Parameters werden auch nachgebaut. Hier müssen wahrscheinlich noch mehr
Properties (Datentypen) abgeschrieben werden. Das Beispiel funktioniert mit
varchar.
Das Beispiel darf gerne getestet und irgendwo veröffentlicht werden..
Lutz Elßner