FYI: I am running on dotnet 3.5 SP1
I am trying to retrieve the value of an identity column into my dataset after performing an update (using a SqlDataAdapter and SqlCommandBuilder).
After performing SqlDataAdapter.Update(myDataset), I want to be able to read the auto-assigned value of myDataset.tables(0).Rows(0)("ID")
, but it is System.DBNull (despite the fact that the row was inserted).
(Note: I do not want to explicitly write a new stored procedure to do this!)
One method often posted http://forums.asp.net/t/951025.aspx modifies the SqlDataAdapter.InsertCommand and UpdatedRowSource like so:
SqlDataAdapter.InsertCommand.CommandText += "; SELECT MyTableID = SCOPE_IDENTITY()"
InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord
Apparently, this seemed to work for many people in the past, but does not work for me.
Another technique: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=619031&SiteID=1 doesn't work for me either, as after executing the SqlDataAdapter.Update, the SqlDataAdapter.InsertCommand.Parameters collection is reset to the original (losing the additional added parameter).
Does anyone know the answer to this???
This is a problem that I've run into before, the bug seems to be that when you call da.Update(ds); the parameters array of the insert command gets reset to the inital list that was created form your command builder, it removes your added output parameters for the identity.
The solution is to create a new dataAdapter and copy in the commands, then use this new one to do your da.update(ds);
like
SqlDataAdapter da = new SqlDataAdapter("select Top 0 " + GetTableSelectList(dt) +
"FROM " + tableName,_sqlConnectString);
SqlCommandBuilder custCB = new SqlCommandBuilder(da);
custCB.QuotePrefix = "[";
custCB.QuoteSuffix = "]";
da.TableMappings.Add("Table", dt.TableName);
da.UpdateCommand = custCB.GetUpdateCommand();
da.InsertCommand = custCB.GetInsertCommand();
da.DeleteCommand = custCB.GetDeleteCommand();
da.InsertCommand.CommandText = String.Concat(da.InsertCommand.CommandText,
"; SELECT ",GetTableSelectList(dt)," From ", tableName,
" where ",pKeyName,"=SCOPE_IDENTITY()");
SqlParameter identParam = new SqlParameter("@Identity", SqlDbType.BigInt, 0, pKeyName);
identParam.Direction = ParameterDirection.Output;
da.InsertCommand.Parameters.Add(identParam);
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
//new adaptor for performing the update
SqlDataAdapter daAutoNum = new SqlDataAdapter();
daAutoNum.DeleteCommand = da.DeleteCommand;
daAutoNum.InsertCommand = da.InsertCommand;
daAutoNum.UpdateCommand = da.UpdateCommand;
daAutoNum.Update(dt);
The insert command can be instructed to update the inserted record using either output parameters or the first returned record (or both) using the UpdatedRowSource property...
InsertCommand.UpdatedRowSource = UpdateRowSource.Both;
If you wanted to use a stored procedure, you'd be done. But you want to use a raw command (aka the output of the command builder), which doesn't allow for either a) output parameters or b) returning a record. Why is this? Well for a) this is what your InsertCommand will look like...
INSERT INTO [SomeTable] ([Name]) VALUES (@Name)
There's no way to enter an output parameter in the command. So what about b)? Unfortunately, the DataAdapter executes the Insert command by calling the commands ExecuteNonQuery method. This does not return any records, so there is no way for the adapter to update the inserted record.
So you need to either use a stored proc, or give up on using the DataAdapter.
I found the answer that works for me here: http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-ado-net/4933/Have-soln-but-need-understanding-return-IDENTITY-issue
Code that worked (from the site - attributed to Girish)
//_dataCommand is an instance of SqlDataAdapter
//connection is an instance of ConnectionProvider which has a property called DBConnection of type SqlConnection
//_dataTable is an instance of DataTable
SqlCommandBuilder bldr = new SqlCommandBuilder(_dataCommand);
SqlCommand cmdInsert = new SqlCommand(bldr.GetInsertCommand().CommandText, connection.DBConnection);
cmdInsert.CommandText += ";Select SCOPE_IDENTITY() as id";
SqlParameter[] aParams = new
SqlParameter[bldr.GetInsertCommand().Parameters.Count];
bldr.GetInsertCommand().Parameters.CopyTo(aParams, 0);
bldr.GetInsertCommand().Parameters.Clear();
for(int i=0 ; i < aParams.Length; i++)
{
cmdInsert.Parameters.Add(aParams[i]);
}
_dataCommand.InsertCommand = cmdInsert;
_dataCommand.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
_dataCommand.Update(_dataTable);
What works for me is configuring a MissingSchemaAction:
SqlCommandBuilder commandBuilder = new SqlCommandBuilder(myDataAdapter);
myDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
This lets me retrieve the primary key (if it is an identity, or autonumber) after an insert.
Good luck.
I had the same problem. It was just solved when I cloned the command generated by the commandbuilder. It looks that even when you change the commandText of the insertcommand, it keeps getting the command generated by the Commandbuilder... Here it's the code that I used to clone the command...
private static void CloneBuilderCommand(System.Data.Common.DbCommand toClone,System.Data.Common.DbCommand repository)
{
repository.CommandText = toClone.CommandText;
//Copying parameters
if (toClone.Parameters.Count == 0) return;//No parameters to clone? go away!
System.Data.Common.DbParameter[] parametersArray= new System.Data.Common.DbParameter[toClone.Parameters.Count];
toClone.Parameters.CopyTo(parametersArray, 0);
toClone.Parameters.Clear();//Removing association before link to the repository one
repository.Parameters.AddRange(parametersArray);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With