1.說明
和EF版本的并發處理方案一致,需要知道樂觀并發和悲觀并發的區別,EF Core只支持樂觀并發;監控并發的兩種方案:監測單個字段和監測整條數據,DataAnnotations 和 FluentApi的兩種配置方式。
?(PS:EF Core中的并發處理模式和EF中的基本類似,其他相關概念參考:第二十節: 深入理解并發機制以及解決方案(鎖機制、EF自有機制、隊列模式等):)
?2. 兩個概念
(1).悲觀并發:比如有兩個用戶A,B,同時登錄系統修改一個文檔,如果A先進入修改,則系統會把該文檔鎖住,B就沒辦法打開了,?只有等A修改完,完全退出的時候B才能進入修改。
(2).樂觀并發:A,B兩個用戶同時登錄,如果A先進入修改緊跟著B也進入了。A修改文檔的同時B也在修改,如果在A保存之后/ B再保存他的修改,此時系統檢測到數據庫中文檔記錄與B剛進入時不一致,B保存時會拋出異常,修改失敗。
3. 監測單個字段
用到的表結構:
(1). 監測那個字段就配置哪個字段,有兩種配置方式,如下:
A. DataAnnotations配置:在對應字段上加特性 [ConcurrencyCheck]
B. FluentApi配置: entity.Property(p => p.age).IsConcurrencyToken();
提交數據庫的時候,發現該字段中的值已經被篡改了,就會進入DbUpdateConcurrencyException并發異常中,這里面可以獲取三個值:數據庫原始值、?數據庫現在值、內存中當前值。
通常的處理方式:
①. Reload一下,放棄當前內存中的實體,重新到數據庫中加載當前實體,然后用當前數據庫中的值進行相應的業務處理。
②. 直接提示用戶,信息被修改,請重新操作一遍(可以更友好的提示一下)
(2).案例:
保證T_Concurrency01有一條id=01的記錄(age=10),分別用上面的兩種方式進行配置,測試的結果最終age=4,執行正確。
1 { 2 //保證T_Concurrency01有一條id=01的記錄(age=10) 3 ypfContext db1 = new ypfContext(); 4 ypfContext db2 = new ypfContext(); 5 6 try 7 { 8 var data1 = db1.T_Concurrency01.Where(u => u.id == "01").FirstOrDefault(); 9 var data2 = db2.T_Concurrency01.Where(u => u.id == "01").FirstOrDefault(); 10 11 data1.age = data1.age - 2; 12 int result1 = db1.SaveChanges(); 13 14 data2.age = data2.age - 4; 15 int result2 = db2.SaveChanges(); //發現age的值和原先查出來的不一致,會拋異常進入cache 16 } 17 catch (DbUpdateConcurrencyException ex) 18 { 19 var entityEntry = ex.Entries.Single(); 20 var original = entityEntry.OriginalValues.ToObject() as T_Concurrency01; //數據庫原始值 10 21 var database = entityEntry.GetDatabaseValues().ToObject() as T_Concurrency01; //數據庫現在值 8 22 var current = entityEntry.CurrentValues.ToObject() as T_Concurrency01; //當前內存值 6 23 entityEntry.Reload(); //放棄當前內存中的實體,重新到數據庫中加載當前實體 24 25 current.age = database.age - 4; //應該拿著當前數據庫實際的值去處理,即8-4=4 26 entityEntry.CurrentValues.SetValues(current); 27 int result3 = db2.SaveChanges(); 28 } 29 }
4. 監測整條數據
用到的表結構:
?
(1). 首先要給該表在數據庫中加一個字段(任意命名即可),通常叫“timeStamp”或“rowVersion”,然后進行配置:
A. DataAnnotations配置:在對應字段上加特性[Timestamp]
B. FluentApi配置: entity.Property(e => e.rowVersion).IsRowVersion();
提交數據庫的時候,發現該表中任意字段中的值被篡改了,就會進入DbUpdateConcurrencyException并發異常中,這里面可以獲取三個值:數據庫原始值、?數據庫現在值、內存中當前值。
通常的處理方式:
①. Reload一下,放棄當前內存中的實體,重新到數據庫中加載當前實體,然后用當前數據庫中的值進行相應的業務處理。
②. 直接提示用戶,信息被修改,請重新操作一遍(可以更友好的提示一下)
(2). 案例
保證T_Concurrency02有一條id=01的記錄(age=10),沒別用上面的兩種方式進行配置,還是用age來進行測試,測試的結果最終age=4,執行正確。
1 { 2 //保證T_Concurrency02有一條id=01的記錄(age=10) 3 ypfContext db1 = new ypfContext(); 4 ypfContext db2 = new ypfContext(); 5 6 try 7 { 8 var data1 = db1.T_Concurrency02.Where(u => u.id == "01").FirstOrDefault(); 9 var data2 = db2.T_Concurrency02.Where(u => u.id == "01").FirstOrDefault(); 10 11 data1.age = data1.age - 2; 12 int result1 = db1.SaveChanges(); 13 14 data2.age = data2.age - 4; 15 int result2 = db2.SaveChanges(); //發現age的值和原先查出來的不一致,會拋異常進入cache 16 } 17 catch (DbUpdateConcurrencyException ex) 18 { 19 var entityEntry = ex.Entries.Single(); 20 var original = entityEntry.OriginalValues.ToObject() as T_Concurrency02; //數據庫原始值 10 21 var database = entityEntry.GetDatabaseValues().ToObject() as T_Concurrency02; //數據庫現在值 8 22 var current = entityEntry.CurrentValues.ToObject() as T_Concurrency02; //當前內存值 6 23 entityEntry.Reload(); //放棄當前內存中的實體,重新到數據庫中加載當前實體 24 25 current.age = database.age - 4; //應該拿著當前數據庫實際的值去處理,即8-4=4 26 entityEntry.CurrentValues.SetValues(current); 27 int result3 = db2.SaveChanges(); 28 } 29 }
?
?
?
!
- 作???????者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲?????明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲?????明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。