可以使用SqlCommandBuilder(不使用Stored Proc)在插入时检索IDENTITY列值吗?
仅供参考:我在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集合重置为原始(丢失添加的添加参数)。
有人知道答案吗???
解决方案
如果这些其他方法对我们不起作用,则.Net提供的工具(SqlDataAdapter)等实际上并没有提供太多有关灵活性的信息。通常,我们需要将其带入一个新的水平,然后手动开始做一些事情。存储过程将是保持使用SqlDataAdapter的一种方法。否则,由于.Net数据库的设计很简单,因此存在局限性,因此我们需要转移到另一个数据访问工具。如果模型不符合他们的设想,则必须在某个点/级别滚动我们自己的代码。
可以指示insert命令使用输出参数或者使用UpdatedRowSource属性更新第一个返回的记录(或者同时更新这两个记录)来更新插入的记录...
InsertCommand.UpdatedRowSource = UpdateRowSource.Both;
如果我们想使用存储过程,就可以了。但是我们想使用原始命令(又称命令构建器的输出),该命令不允许a)输出参数或者b)返回记录。为什么是这样?好吧,这是a)这就是InsertCommand的样子...
INSERT INTO [SomeTable] ([Name]) VALUES (@Name)
无法在命令中输入输出参数。那么b)呢?不幸的是,DataAdapter通过调用命令ExecuteNonQuery方法来执行Insert命令。这不会返回任何记录,因此适配器无法更新插入的记录。
因此,我们需要使用存储的proc或者放弃使用DataAdapter。
我们是否考虑过使用LINQ?我知道这不能解决实际问题,但是如果我们使用的是.NET 3.5,则应该尝试使用LINQ。实际上,随着Code First EntityFramework的出现,我认为我们可以轻松地选择LINQ to SQL或者EF作为滚动自己的DAL的相对轻量级的选择。
实际上,这对我有用:
SqlDataAdapter.InsertCommand.CommandText += "; SELECT MyTableID = SCOPE_IDENTITY()" InsertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters;
干杯。
我有同样的问题。当我克隆了commandbuilder生成的命令时,就解决了。看起来,即使我们更改了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); }
对我有用的是配置MissingSchemaAction:
SqlCommandBuilder commandBuilder = new SqlCommandBuilder(myDataAdapter); myDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;
这使我可以在插入后检索主键(如果它是标识或者自动编号)。
祝你好运。
如果我们只想(a)将一条记录插入表中,(b)使用数据集,并且(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