在解說 DBConcurrencyException 之前有一些觀念必須先說清楚, 所以就先講一些必要的觀念, 最後在說明主題.
1.使用 DataAdapter.Update 時,DataAdapter 會分析已進行的變更,並執行適當的命令 (INSERT、UPDATE 或 DELETE), 詳細的MSDN文章說明參考 : 以 DataAdapter 更新資料來源. 一般的程式這樣就夠用了.
程式範例如下 :
DataAdapter.Fill(DataSet, "product"); OracleCommandBuilder builder = new OracleCommandBuilder(odaPROD); //沒有 CommandBuilder Adapter 會不知道怎樣更新資料, 就會產生 InvalidOperationException 如下 //System.InvalidOperationException: Update requires a valid UpdateCommand when passed DataRow collection with modified rows. |
2. 再來看看 CommandBuilder 產生的 SQL 是怎樣. 詳細的MSDN文章說明參考 : 自動產生命令
odaPROD.RowUpdating += new OracleRowUpdatingEventHandler(OnRowUpdating);
|
3. 如果是網頁程式或是 MultiThread 可能會遇到 DBConcurrencyException. 概念是兩個使用者同時修改相同資料, 在儲存時發現資料已經被另一個使用者更新了. 這種情況非常容易模擬, 只要將程式停在 DataAdapter.Update, 然後連線到 DB 將隨便一個欄位異動後 commit 再執行 DataAdapter.Update 及產生 DBConcurrencyException. 要避免 DBConcurrencyException 只要指定合適的 UpdateCommand 即可 ( 但可能還會有 DB Lock 問題 ).
odaPROD = new OracleDataAdapter("select * from PROD where prod_id='N0001'", OracleCon); odaPROD.Fill(dsPROD, "PROD"); odaPROD.UpdateCommand = new OracleCommand("UPDATE PROD SET CREATE_DATE=:CREATE_DATE WHERE PROD_ID=:PROD_ID", OracleCon); OracleParameter Par_CREATE_DATE = odaPROD.UpdateCommand.Parameters.Add("CREATE_DATE", OracleDbType.Date); Par_CREATE_DATE.SourceColumn = "CREATE_DATE"; Par_CREATE_DATE.SourceVersion = DataRowVersion.Current; OracleParameter Par_PROD_ID = odaPROD.UpdateCommand.Parameters.Add("PROD_ID", OracleDbType.Varchar2, 10); Par_PROD_ID.SourceColumn = "PROD_ID"; Par_PROD_ID.SourceVersion = DataRowVersion.Original; |