可以使用SqlCommandBuilder(不使用Stored Proc)在插入时检索IDENTITY列值吗?

时间:2020-03-06 14:45:14  来源:igfitidea点击:

仅供参考:我在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