仅供参考:我在dotnet 3.5 SP1上运行
我正在尝试在执行更新后使用SqlDataAdapter和SqlCommandBuilder将标识列的值检索到我的数据集中。
执行SqlDataAdapter.Update(myDataset)后,我希望能够读取myDataset.tables(0).Rows(0)("ID")
的自动赋值,但它是System.DBNull(尽管插入了行的事实)。
(注意:我不想显式编写新的存储过程来执行此操作!)
经常发布的一个方法http://forums.asp.net/t/951025.aspx修改了SqlDataAdapter.InsertCommand和UpdatedRowSource,如下所示:
SqlDataAdapter.InsertCommand.CommandText += "; SELECT MyTableID = SCOPE_IDENTITY()"
InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord
显然,这似乎对过去很多人有用,但对我不起作用。
另一种技术:http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=619031&SiteID=1对我来说也不起作用,因为在执行SqlDataAdapter.Update之后,SqlDataAdapter.InsertCommand.Parameters集合被重置为原始集合(丢失了额外添加的参数)。
有谁知道答案???
答案 0 :(得分:10)
这是我之前遇到的一个问题,当你打电话时,这个错误似乎就是这个问题 da.Update(DS); insert命令的parameters数组被重置为从命令构建器创建的初始列表,它会删除您为标识添加的输出参数。
解决方案是创建一个新的dataAdapter并在命令中复制,然后使用这个新的来执行da.update(ds);
像
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);
答案 1 :(得分:4)
可以使用UpdatedRowSource属性指示insert命令使用输出参数或第一个返回的记录(或两者)更新插入的记录...
InsertCommand.UpdatedRowSource = UpdateRowSource.Both;
如果您想使用存储过程,那么您就完成了。但是您希望使用原始命令(也就是命令构建器的输出),它不允许a)输出参数或b)返回记录。为什么是这样?好吧a)这就是你的InsertCommand看起来像......
INSERT INTO [SomeTable] ([Name]) VALUES (@Name)
无法在命令中输入输出参数。那么b)呢?不幸的是,DataAdapter通过调用ExecuteNonQuery方法命令来执行Insert命令。这不会返回任何记录,因此适配器无法更新插入的记录。
所以你需要使用存储过程,或者放弃使用DataAdapter。
答案 2 :(得分:4)
对我来说有用的是配置MissingSchemaAction:
SqlCommandBuilder commandBuilder = new SqlCommandBuilder(myDataAdapter);
myDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
这使我可以在插入后检索主键(如果它是标识或自动编号)。
祝你好运。答案 3 :(得分:2)
实际上,这对我有用:
SqlDataAdapter.InsertCommand.CommandText += "; SELECT MyTableID = SCOPE_IDENTITY()" InsertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
干杯。
答案 4 :(得分:2)
我遇到了同样的问题。当我克隆命令生成器生成的命令时,它才刚刚解决。看起来即使你更改insertcommand的commandText,它仍然会获得Commandbuilder生成的命令... 这是我用来克隆命令的代码......
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);
}
答案 5 :(得分:2)
如果您只想(a)将一条记录插入表中,(b)使用DataSet,(c)不使用存储过程,则可以按照以下步骤操作:
创建dataAdapter,但在select语句中添加WHERE 1 = 0,这样就不必下载整个表 - 性能的可选步骤
使用范围标识选择语句和输出参数创建自定义INSERT语句。
进行正常处理,填充数据集,添加记录并保存表格更新。
现在应该可以直接从参数中提取身份。
示例:
'-- post a new entry and return the column number
' get the table stucture
Dim ds As DataSet = New DataSet()
Dim da As SqlDataAdapter = New SqlDataAdapter(String.Concat("SELECT * FROM [", fileRegisterSchemaName, "].[", fileRegisterTableName, "] WHERE 1=0"), sqlConnectionString)
Dim cb As SqlCommandBuilder = New SqlCommandBuilder(da)
' since we want the identity column back (FileID), we need to write our own INSERT statement
da.InsertCommand = New SqlCommand(String.Concat("INSERT INTO [", fileRegisterSchemaName, "].[", fileRegisterTableName, "] (FileName, [User], [Date], [Table]) VALUES (@FileName, @User, @Date, @Table); SELECT @FileID = SCOPE_IDENTITY();"))
da.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord
With da.InsertCommand.Parameters
.Add("@FileName", SqlDbType.VarChar, 1024, "FileName")
.Add("@User", SqlDbType.VarChar, 24, "User")
.Add("@Date", SqlDbType.DateTime, 0, "Date")
.Add("@Table", SqlDbType.VarChar, 128, "FileName")
' allow the @FileID to be returned back to us
.Add("@FileID", SqlDbType.Int, 0, "FileID")
.Item("@FileID").Direction = ParameterDirection.Output
End With
' copy the table structure from the server and create a reference to the table(dt)
da.Fill(ds, fileRegisterTableName)
Dim dt As DataTable = ds.Tables(fileRegisterTableName)
' add a new record
Dim dr As DataRow = dt.NewRow()
dr("FileName") = fileName
dr("User") = String.Concat(Environment.UserDomainName, "\", Environment.UserName)
dr("Date") = DateTime.Now()
dr("Table") = targetTableName
dt.Rows.Add(dr)
' save the new record
da.Update(dt)
' return the FileID (Identity)
Return da.InsertCommand.Parameters("@FileID").Value
但是那很长时间才能做到与此相同的事情......
' add the file record
Dim sqlCmd As SqlCommand = New SqlCommand(String.Concat("INSERT INTO [", fileRegisterSchemaName, "].[", fileRegisterTableName, "] (FileName, [User], [Date], [Table]) VALUES (@FileName, @User, @Date, @Table); SELECT SCOPE_IDENTITY();"), New SqlConnection(sqlConnectionString))
With sqlCmd.Parameters
.AddWithValue("@FileName", fileName)
.AddWithValue("@User", String.Concat(Environment.UserDomainName, "\", Environment.UserName))
.AddWithValue("@Date", DateTime.Now())
.AddWithValue("@Table", targetTableName)
End With
sqlCmd.Connection.Open()
Return sqlCmd.ExecuteScalar
答案 6 :(得分:2)
我在这里找到了适合我的答案:http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-ado-net/4933/Have-soln-but-need-understanding-return-IDENTITY-issue
有效的代码(来自网站 - 归因于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);
答案 7 :(得分:1)
如果那些其他方法不起作用,.Net提供的工具(SqlDataAdapter)等在灵活性上并没有提供太多其他方法。您通常需要将它提升到一个新的水平并开始手动完成。存储过程将是继续使用SqlDataAdapter的一种方法。否则,您需要转移到另一个数据访问工具,因为.Net数据库有限制,因为它们设计得很简单。如果您的模型不符合他们的愿景,您必须在某个点/级别滚动您自己的代码。
答案 8 :(得分:1)
问题在于我放置了代码。
在RowUpdating事件处理程序中添加代码,如下所示:
void dataSet_RowUpdating(object sender, SqlRowUpdatingEventArgs e)
{
if (e.StatementType == StatementType.Insert)
{
e.Command.CommandText += "; SELECT ID = SCOPE_IDENTITY()";
e.Command.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
}
}
答案 9 :(得分:0)
仅使用命令对象的另一种方式。
Dim sb As New StringBuilder
sb.Append(" Insert into ")
sb.Append(tbl)
sb.Append(" ")
sb.Append(cnames)
sb.Append(" values ")
sb.Append(cvals)
sb.Append(";Select SCOPE_IDENTITY() as id") 'add identity selection
Dim sql As String = sb.ToString
Dim cmd As System.Data.Common.DbCommand = connection.CreateCommand
cmd.Connection = connection
cmd.CommandText = sql
cmd.CommandType = CommandType.Text
cmd.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord
'retrieve the new identity value, and update the object
Dim dec as decimal = CType(cmd.ExecuteScalar, Decimal)
答案 10 :(得分:0)
实际上,它更容易,不需要任何自定义命令。在数据集设计器中创建tableadapter时,请指定&#34;刷新数据表&#34;在向导的高级选项中。在此之后,在发出dataadapter.update mydataset之后,您将在数据表中找到使用数据库中的新值填充的标识列。
答案 11 :(得分:0)
我的解决方案:
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Mytable;" , sqlConnection);
SqlCommandBuilder cb = new SqlCommandBuilder(da);
da.DeleteCommand = (SqlCommand)cb.GetDeleteCommand().Clone();
da.UpdateCommand = (SqlCommand)cb.GetUpdateCommand().Clone();
SqlCommand insertCmd = (SqlCommand)cb.GetInsertCommand().Clone();
insertCmd.CommandText += ";SET @Id = SCOPE_IDENTITY();";
insertCmd.Parameters.Add("@Id", SqlDbType.Int, 0, "Id").Direction = ParameterDirection.Output;
da.InsertCommand = insertCmd;
da.InsertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
cb.Dispose();
答案 12 :(得分:0)
问题是在使用 SqlDataAdapter.Update(DataSet) 方法时没有考虑您对 InsertCommand 所做的更改. 完美的解决方案是克隆 CommandBuilder 生成的 InsertCommand。
# Initialising the variables
$Array = @()
$Server = (Get-ComputerInfo).CsName
$measurecmd = (measure-command { 1..1000 | % {random} | sort }).Seconds
$datetime = Get-Date
$sortsec = "1000"
$LogTime = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$LogFile = 'C:\Avanthi\PS_script_'+$LogTime+".log"
# Starting the timer to iterate the output till session is closed
$timer = [Diagnostics.Stopwatch]::StartNew()
while ($timer.Elapsed.TotalSeconds -lt 1800) {
Try {
# Processor utilization
$Processor = (Get-WmiObject -ComputerName $Server -Class win32_processor -ErrorAction Stop | Measure-Object -Property LoadPercentage -Average | Select-Object Average).Average
# Memory utilization
$ComputerMemory = Get-WmiObject -ComputerName $Server -Class win32_operatingsystem -ErrorAction Stop
$Memory = ((($ComputerMemory.TotalVisibleMemorySize - $ComputerMemory.FreePhysicalMemory)*100)/ $ComputerMemory.TotalVisibleMemorySize)
$RoundMemory = [math]::Round($Memory, 2)
# Creating custom object
# MachineName Date Time sort100kseconds
$Object = New-Object PSCustomObject
$Object | Add-Member -MemberType NoteProperty -Name "Server name" -Value $Server
$datetime = Get-Date
$Object | Add-Member -MemberType NoteProperty -Name "Date Time" -Value $datetime
$Object | Add-Member -MemberType NoteProperty -Name "CPU %" -Value $Processor
$Object | Add-Member -MemberType NoteProperty -Name "Memory %" -Value $RoundMemory
$Object | Add-Member -MemberType NoteProperty -Name "Meaure CPU speed" -Value (measure-command { 1..1000 | % {random} | sort }).Seconds
$Object | Add-Member -MemberType NoteProperty -Name "Sort Seconds" -Value $sortsec
$Object
$Array += $Object
}
Catch {
Write-Host "Something went wrong ($Server): "$_.Exception.Message
Continue
}
#Final results - Writing to Log file
If ($Array) {
$Array | Out-File $LogFile -Force
}
# Sleep for amount of seconds
Start-Sleep -Seconds 60
}
答案 13 :(得分:-1)
您是否考虑过使用LINQ?我理解这并不能解决您的实际问题,但如果您使用的是.NET 3.5,那么您真的应该尝试使用LINQ。事实上,随着Code First EntityFramework的出现,我认为你可以轻松地选择LINQ to SQL或EF作为滚动你自己的DAL的相对轻量级替代品。