文章目錄
如何避免多個 Entity Framework 6 instance 造成資料覆蓋問題 (DB First - Oracle)
之前筆記 如何避免多個 Entity Framework 6 instance 造成資料覆蓋問題 (DB First - SQL Server) 提到 Entity Framework 使用上的限制,也紀錄如何透過在 SQL Server 的 table 上加入 rowversion 的欄位並將 Entity Framework 上該欄位的 Concurrency Mode 改為 fixed 就可以避免修改資料後遭其他人覆寫的狀況,那相同問題當然也會發生在 Oracle 上,就來看看 Oracle 該如何解決吧
重現問題
- user A 開啟特定一筆資料 (name 為 Yowko) 想要修改內容
- user B 也想修改同一筆資料 (name 為 Yowko)
修改情境 (同時開啟資料,僅存檔順序不同)
user B 將 salary 改為
100並存檔,user A 則改為110並存檔
user A 將 salary 改為
110並存檔,user B 則改為100並存檔
user B 將 salary 改為
100並存檔,user A 則修改 name 為Yowko Tsai並存檔
user A 將 name 為
Yowko Tsai並存檔,user B 則修改 salary 為100並存檔
結果為何?
user B 將 salary 改為 100 並存檔,user A 則改為 110 並存檔

user A 將 salary 改為 110 並存檔,user B 則改為 100 並存檔

user B 將 salary 改為 100 並存檔,user A 則修改 name 為 Yowko Tsai 並存檔

user A 將 name 為 Yowko Tsai 並存檔,user B 則修改 salary 為 100 並存檔

看完上面幾種情境真實操作的結果,我想應該沒有人可以接受,所以接著就來看看該如何設定
設定步驟
DB 相關設定
建立 table
加入
ROWVERSIONCREATE TABLE "TEST"."USERS" ( "ID" NUMBER(10,0) NOT NULL ENABLE, "NAME" VARCHAR2(50 BYTE), "SALARY" NUMBER(10,0), "ROWVERSION" NUMBER(*,0) NOT NULL ENABLE, CONSTRAINT "ID_PK" PRIMARY KEY ("ID") );建立
SEQUENCEforID跟ROWVERSION似 SQL Server 自動序號
ID
CREATE SEQUENCE "TEST"."SQ_USERSID" MINVALUE 1 MAXVALUE 9999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE ;ROWVERSION
CREATE SEQUENCE "TEST"."SQ_USERSROWVERSION" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE ;
建立 trigger for
ID跟ROWVERSIONID
使用 trigger 在 insert 資料時取得自動序號填入
create or replace trigger TEST.TR_USERSID before insert on TEST.USERS for each row begin select TEST.SQ_USERSID.nextval into :new.Id from dual; end;ROWVERSION
使用 trigger 在 insert 及 update 資料時取得自動序號修改值
create or replace trigger TEST.TR_USERSROWVERSION before insert or update on TEST.USERS for each row begin :NEW.ROWVERSION := TEST.SQ_USERSROWVERSION.NEXTVAL; end;
為什麼不使用
ORA_ROWSCN建立 table 時加上
ROWDEPENDENCIES10 g 開始可以使用
ORA_ROWSCN,但需要特別注意的是ORA_ROWSCN預設是透過 block 來追蹤變更,不是絕對的精準,可以在建立 table 時加上ROWDEPENDENCIES調整為追蹤 rowCREATE TABLE "TEST"."USERS" ( "ID" NUMBER(10,0) NOT NULL ENABLE, "NAME" VARCHAR2(50 BYTE), "SALARY" NUMBER(10,0), CONSTRAINT "ID_PK" PRIMARY KEY ("ID") ) ROWDEPENDENCIES ;11 g 尚未支援 alter 方式改追蹤 row
ORA_ROWSCN並非欄位屬性,需要直接從 script 動手才能取得與 Entity Framework db first 不好整合,需要手動加入
DefiningQuery與ModificationFunctionMapping,使用上討不到便利SELECT ORA_ROWSCN as "rowversion", "USERS"."ID" AS "ID", "USERS"."NAME" AS "NAME", "USERS"."SALARY" AS "SALARY" FROM "TEST"."USERS" "USERS"
Entity Framework 設定
將 table 加入 edmx 中

將
ROWVERSION欄位 Concurrency Model 屬性改為Fixed
調整 View (Edit)
讓
ROWVERSION成為隱藏欄位@Html.HiddenFor(model=>model.ROWVERSION)針對資料過時錯誤做特別處理
try { await db.SaveChangesAsync(); } catch (DbUpdateConcurrencyException ex) { return Content($"<script>alert('資料已被他人修改,請重新編輯');document.location = '{Url.Action("Edit",user.ID)}'</script>"); }實際效果

心得
原本興高采烈以為 Oracle 也有跟 SQL Server 一樣的 rowversion 欄位屬性,可以延續過去處理並行衝突的經驗:設定 Concurrency 為 Fixed 即可,但實作後發現兩者終究是不同的設計概念, Oracle 不僅預設是追蹤 block 的變更再加上並非是欄位屬性,讓實際與 Entity Framework 整合時便利性大打折扣 (需要手動修改 edmx 的 DefiningQuery 與 ModificationFunctionMapping 區段,另外還需要有相關 Insert、Update、Delete DB function or storedprocedure 才能真正達成目的),所以經過許多嘗試後還是決定放棄使用 ORA_ROWSCN,改透過 trigger 來改變 ROWVERSION的值,雖然看似沒那麼漂亮,不過使用起來反而比 ORA_ROWSCN 簡單許多XD
參考資訊
文章作者 Yowko Tsai
上次更新 2021-11-03
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。
