Linq to sql 檢測併發

2021-06-17 22:28:44 字數 4545 閱讀 1416

首先使用下面的

sql

語句查詢資料庫的產品表:

select

* from

products

where

categoryid=1

為了看起來清晰,我已經事先把所有分類為

1 產品的**和庫存修改為相同值了。然

後執行下面的程式:

varquery =

from

p in

ctx.products

where

p.categoryid == 1

select

p;

foreach

(var

p in

query)

p.unitsinstock =

convert

.toint16(p.unitsinstock - 1);

ctx.submitchanges(); // 

在這裡設斷點

我們使用除錯方式啟動,由於設定了斷點,程式並沒有進行更新操作。此時,我們在數

據庫中執行下面的語句:

update

products 

setunitsinstock

=unitsinstock -2

,unitprice

=unitprice +1

where

categoryid =1

然後在繼續程式,會得到修改併發(樂觀併發衝突)的異常,提示要修改的行不存在或

者已經被改動。當客戶端提交的修改物件自讀取之後已經在資料庫中發生改動,就產生了修

改併發。解決併發的包括兩步,一是查明哪些物件發生併發,二是解決併發。如果你僅僅是

希望更新時不考慮併發的話可以關閉相關列的更新驗證,這樣在這些列上發生併發就不會出

現異常:

[column

(storage=

"_unitsinstock"

, dbtype=

"smallint"

, updatecheck =

updatecheck

.never)]

[column

(storage=

"_unitprice"

, dbtype=

"money"

, updatecheck =

updatecheck

.never)]

為這兩列標註不需要進行更新檢測。假設現在產品**和庫存分別是

27 和

32。那麼,

我們啟動程式(設定端點),然後執行

update

語句,把**

+1,庫存

-2,然後**和庫

存分別為

28 和

30 了,繼續程式可以發現**和庫存分別是

28 和

31。**

+1 是之前更新

的功勞,庫存最終是

-1 是我們程式之後更新的功勞。當在同乙個欄位上(庫存)發生併發

衝突的時候,預設是最後的那次更新獲勝。

解決併發

如果你希望自己處理併發的話可以把前面對列的定義修改先改回來,看下面的例子:

varquery =

from

p in

ctx.products

where

p.categoryid == 1

select

p;

foreach

(var

p in

query)

p.unitsinstock =

convert

.toint16(p.unitsinstock - 1);

try

catch

(changeconflictexception)

} ctx.submitchanges();

首先可以看到,我們使用

try{}catch{}

來捕捉併發衝突的異常。在

submitchanges 的時

候,我們選擇了

conflictmode.continueonconflict

選項。也就是說遇到併發了還是繼續。

在catch{}

中,我們從

changeconflicts

中獲取了併發的物件,然後經過型別轉化後輸出了

產品id

,然後選擇的解決方案是

refreshmode.overwritecurrentvalues

。也就是說,放棄

當前的更新,所有更新以原先更新為準。

我們來測試一下,假設現在產品**和庫存分別是

27 和

32。那麼,我們啟動程式(在

ctx.submitchanges(conflictmode.continueonconflict)

這裡設定端點),然後執行

update

語句,把**

+1,庫存

-2,然後**和庫存分別為

28 和

30 了,繼續程式可以發現**和

庫存分別是

28 和

30。之前

sql

語句庫存

-2 生效了,而我們程式的更新(庫存

-1)被放棄

了。在頁面上也顯示了所有分類為

1 的產品

id(因為我們之前的

sql

語句是對所有分類為

1 的產品都進行修改的)。

然後,我們來修改一下解決併發的方式:

cc.resolve(

refreshmode

.keepcurrentvalues);

// 

放棄原先更新,所有更新以當前更新為

準 來測試一下,假設現在產品**和庫存分別是

27 和

32。那麼,我們啟動程式(在

ctx.submitchanges(conflictmode.continueonconflict)

這裡設定端點),然後執行

update

語句,把**

+1,庫存

-2,然後**和庫存分別為

28 和

30 了,繼續程式可以發現**和

庫存分別是

27 和

31。產品**沒有變化,庫存

-1 了,都是我們程式的功勞,

sql

語句的

更新被放棄了。

然後,我們再來修改一下解決併發的方式:

cc.resolve(

refreshmode

.keepchanges);

// 

原先更新有效,衝突欄位以當前更新為準

來測試一下,假設現在產品**和庫存分別是

27 和

32。那麼,我們啟動程式(在

ctx.submitchanges(conflictmode.continueonconflict)

這裡設定端點),然後執行

update

語句,把**

+1,庫存

-2,然後**和庫存分別為

28 和

30 了,繼續程式可以發現**和

庫存分別是

28 和

31。這就是預設方式,在保持原先更新的基礎上,對於發生衝突的字段以

最後更新為準。

我們甚至還可以針對不同的字段進行不同的處理策略:

foreach

(objectchangeconflict

cc in

ctx.changeconflicts)

}比如上述**就對庫存欄位作放棄原先更新處理,對**欄位作放棄當前更新處理。我

們來測試一下,假設現在產品**和庫存分別是

27 和

32。那麼,我們啟動程式(在

ctx.submitchanges(conflictmode.continueonconflict)

這裡設定端點),然後執行

update

語句,把**

+1,庫存

-2,然後**和庫存分別為

28 和

30 了,繼續程式可以發現**和

庫存分別為

28 和

31 了。說明對**的處理確實保留了原先的更新,對庫存的處理保留了

當前的更新。頁面上顯示的結果如下圖:

最後,我們把提交語句修改為:

ctx.submitchanges(

conflictmode

.failonfirstconflict);

表示第一次發生衝突的時候就不再繼續了,然後並且去除最後的

ctx.submitchanges();

語句。來測試一下,在執行了

sql

後再繼續程式可以發現介面上只輸出了數字

1,說明在

第一條記錄失敗後,後續的併發衝突就不再處理了。

LINQ TO SQL 併發控制

column特性的updatacheck用於設定併發衝突處理方式 always 使用使用這個列進行衝突檢測 never 從不使用這個列進行衝突檢測 whenchanged 僅在成員被應用程式更改時使用這個列檢測 這裡的檢測指傳送的sql中的where中的條件,如userinfo表中name列進行衝突...

LinQ to SQL 併發與事務 1

首先使用下面的sql語句查詢資料庫的產品表 select from products where categoryid 1 查詢結果如下圖 為了看起來清晰,我已經事先把所有分類為1產品的 和庫存修改為相同值了。然後執行下面的程式 var query from p in ctx.products wh...

LinQ to SQL 併發與事務 3

最後,我們把提交語句修改為 ctx.submitchanges conflictmode.failonfirstconflict 表示第一次發生衝突的時候就不再繼續了,然後並且去除最後的ctx.submitchanges 語句。來測試一下,在執行了sql後再繼續程式可以發現介面上只輸出了數字1,說明...