2008-09-25 29 views
13

僅供參考:我在dotnet上運行3.5 SP1使用SqlCommandBuilder(不使用Stored Proc)插入時可能檢索IDENTITY列值?

我試圖在執行更新(使用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

只爲參考:(代碼項目的文章「[與CommandBuilder的和DataAdapter使用SCOPE_IDENTITY] http://www.codeproject.com/Tips/288850/Using-SCOPE- IDENTITY-with-CommandBuilder-and-DataA)「是幫助我的解決方案。 (我不是這篇文章的作者)。 – 2014-10-22 14:47:23

+0

我知道這是舊的,但在2015年,這仍然是非常錯誤的。我有同樣的問題,並已使用上述文章中的代碼。對我來說,解決的辦法是將「將ID設置爲SCOPE_IDENTITY()」改爲「SET SCOPE_ID = SCOPE_IDENTITY()」是的我知道沒有真正的區別,但現在由於某種原因再次起作用 – GuidoG 2017-04-10 07:36:40

回答

1

如果這些其他方法不適合你,那麼.Net提供的工具(SqlDataAdapter)等實際上並沒有提供有關靈活性的其他方法。您通常需要將其升級到下一個級別,然後開始手動進行操作。存儲過程將是繼續使用SqlDataAdapter的一種方法。否則,你需要移動到另一個數據訪問工具,因爲它們的設計簡單,.Net數據庫有限制。如果你的模型不符合他們的願景,你必須在某個點/層面推出自己的代碼。

4

插入命令可以指示使用任何輸出參數或使用UpdatedRowSource屬性的第一個返回的記錄(或兩者)來更新插入的記錄...

InsertCommand.UpdatedRowSource = UpdateRowSource.Both; 

如果你想使用存儲過程,你會完成的。但是你想使用原始命令(aka命令生成器的輸出),它不允許a)輸出參數或b)返回記錄。爲什麼是這樣?好了)這是你的InsertCommand的樣子...

INSERT INTO [SomeTable] ([Name]) VALUES (@Name) 

有沒有辦法進入該命令的輸出參數。那麼b)呢?不幸的是,DataAdapter通過調用ExecuteNonQuery方法來執行Insert命令。這不會返回任何記錄,因此適配器無法更新插入的記錄。

因此,您需要使用存儲過程,或放棄使用DataAdapter。

+0

「DataAdapter通過調用命令ExecuteNonQuery方法「.....我想這將解釋我所看到的行爲,但其他人是如何得到我發佈的示例技術工作? UpdatedRowSource屬性的目的是什麼? – tbone 2008-09-26 14:23:09

+0

我看到的所有示例都使用存儲過程。 UpdatedRowSource適用於存儲過程。沒有使用存儲過程的人沒有使用DataAdapter。 – dbugger 2008-10-02 02:26:51

-1

你有沒有用LINQ來看看?我知道這並不能解決你的實際問題,但如果你使用的是.NET 3.5,你應該嘗試使用LINQ。實際上,隨着Code First EntityFramework的出現,我認爲您可以輕鬆選擇LINQ to SQL或EF作爲相對輕量級的替代方案來滾動您自己的DAL。

2

其實,這個工作對我來說:

SqlDataAdapter.InsertCommand.CommandText += "; SELECT MyTableID = SCOPE_IDENTITY()" InsertCommand.UpdatedRowSource = UpdateRowSource.OutputParameters; 

乾杯。

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);    
    } 
3

我是配置MissingSchemaAction:

SqlCommandBuilder commandBuilder = new SqlCommandBuilder(myDataAdapter); 
myDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey; 

這讓我找回了主鍵(如果它是一個身份,或自動編號)插入之後。

祝你好運。

2

如果你只是想(一)插入一條記錄到表;(二)使用DataSet和(c)不使用存儲過程,那麼你可以遵循這樣的:

  1. 創建dataAdapter,但在select語句中添加WHERE 1 = 0,這樣就不必下載整個表格 - 性能可選步驟

  2. 使用作用域標識select語句和輸出參數創建自定義INSERT語句。

  3. 做正常的處理,填充數據集,添加記錄和保存表更新。

  4. 現在應該能夠直接從參數中提取標識。

例子:

'-- 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 
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); 
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) 
8

這是我碰到的前一個問題,這個bug似乎是,當你調用 da.Update(DS); insert命令的參數數組重置爲從命令構建器創建的初始列表,它刪除了添加的標識輸出參數。

解決方案是創建一個新的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

這個問題對我來說是哪裏的代碼放置。

添加代碼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; 
    } 
} 
0

事實上,那就更簡單了,在任何自定義命令沒有必要。在數據集設計器中創建tableadapter時,請在嚮導的「高級選項」中指定「刷新數據表」。因此,在發出dataadapter.update mydataset之後,您將在數據表中找到用來自數據庫的新值填充的標識列。

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(); 
相關問題