在解說 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);
//指定RowUpdateing event, 以便取得當時的 SQL.
odaPROD.UpdateCommand = builder.GetUpdateCommand();
...
protected void OnRowUpdating(object sender, OracleRowUpdatingEventArgs e)
{
    Console.WriteLine("Command Text: " + e.Command.CommandText);
    Console.WriteLine("Command Type: " + e.StatementType);
}


//結果如下 : SQL 有夠複雜, 簡而言之就是所有欄位都更新, 且所有欄位拿來當條件.
//Command Text: UPDATE "PROD" SET "PROD_ID"=:1, "PROD_NAME"=:2, "CREATE_DATE"=:3,"PRICE"=:4 WHERE "PROD_ID"=:5 AND ((:6 = 1 AND "PROD_NAME" IS NULL) OR "PROD_NAME"=:7) AND ((:8 = 1 AND "CREATE_DATE" IS NULL) OR "CREATE_DATE"=:9) AND ((:10 =1 AND "PRICE" IS NULL) OR "PRICE"=:11)
//Command Type: Update
//
//PRODUCT table schema
//(    PROD_ID VARCHAR2(10 BYTE) NOT NULL ENABLE, 
//    PROD_NAME VARCHAR2(64 BYTE), 
//    CREATE_DATE DATE, 
//    PRICE NUMBER, 
//    CONSTRAINT PK_PROD PRIMARY KEY (PROD_ID)
//);

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;
全站熱搜
創作者介紹
創作者 py3939 的頭像
py3939

老爹寫程式

py3939 發表在 痞客邦 留言(0) 人氣()