国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
一步一步學(xué)Linq to sql(七):并發(fā)與事務(wù)
檢測并發(fā)
首先使用下面的SQL語句查詢數(shù)據(jù)庫的產(chǎn)品表:
select * from products where categoryid=1
查詢結(jié)果如下圖:
為了看起來清晰,我已經(jīng)事先把所有分類為1產(chǎn)品的價格和庫存修改為相同值了。然后執(zhí)行下面的程序:
var query = 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(); // 在這里設(shè)斷點
我們使用調(diào)試方式啟動,由于設(shè)置了斷點,程序并沒有進行更新操作。此時,我們在數(shù)據(jù)庫中運行下面的語句:
update products
set unitsinstock = unitsinstock -2, unitprice= unitprice + 1
where categoryid = 1
然后在繼續(xù)程序,會得到修改并發(fā)(樂觀并發(fā)沖突)的異常,提示要修改的行不存在或者已經(jīng)被改動。當(dāng)客戶端提交的修改對象自讀取之后已經(jīng)在數(shù)據(jù)庫中發(fā)生改動,就產(chǎn)生了修改并發(fā)。解決并發(fā)的包括兩步,一是查明哪些對象發(fā)生并發(fā),二是解決并發(fā)。如果你僅僅是希望更新時不考慮并發(fā)的話可以關(guān)閉相關(guān)列的更新驗證,這樣在這些列上發(fā)生并發(fā)就不會出現(xiàn)異常:
[Column(Storage="_UnitsInStock", DbType="SmallInt", UpdateCheck = UpdateCheck.Never)]
[Column(Storage="_UnitPrice", DbType="Money", UpdateCheck = UpdateCheck.Never)]
為這兩列標注不需要進行更新檢測。假設(shè)現(xiàn)在產(chǎn)品價格和庫存分別是27和32。那么,我們啟動程序(設(shè)置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為28和30了,繼續(xù)程序可以發(fā)現(xiàn)價格和庫存分別是28和31。價格+1是之前更新的功勞,庫存最終是-1是我們程序之后更新的功勞。當(dāng)在同一個字段上(庫存)發(fā)生并發(fā)沖突的時候,默認是最后的那次更新獲勝。
解決并發(fā)
如果你希望自己處理并發(fā)的話可以把前面對列的定義修改先改回來,看下面的例子:
var query = from p in ctx.Products where p.CategoryID == 1 select p;
foreach (var p in query)
p.UnitsInStock = Convert.ToInt16(p.UnitsInStock - 1);
try
{
ctx.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException)
{
foreach (ObjectChangeConflict cc in ctx.ChangeConflicts)
{
Product p = (Product)cc.Object;
Response.Write(p.ProductID + "<br/>");
cc.Resolve(RefreshMode.OverwriteCurrentValues); // 放棄當(dāng)前更新,所有更新以原先更新為準
}
}
ctx.SubmitChanges();
首先可以看到,我們使用try{}catch{}來捕捉并發(fā)沖突的異常。在SubmitChanges的時候,我們選擇了ConflictMode.ContinueOnConflict選項。也就是說遇到并發(fā)了還是繼續(xù)。在catch{}中,我們從ChangeConflicts中獲取了并發(fā)的對象,然后經(jīng)過類型轉(zhuǎn)化后輸出了產(chǎn)品ID,然后選擇的解決方案是RefreshMode.OverwriteCurrentValues。也就是說,放棄當(dāng)前的更新,所有更新以原先更新為準。
我們來測試一下,假設(shè)現(xiàn)在產(chǎn)品價格和庫存分別是27和32。那么,我們啟動程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)這里設(shè)置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為28和30了,繼續(xù)程序可以發(fā)現(xiàn)價格和庫存分別是28和30。之前SQL語句庫存-2生效了,而我們程序的更新(庫存-1)被放棄了。在頁面上也顯示了所有分類為1的產(chǎn)品ID(因為我們之前的SQL語句是對所有分類為1的產(chǎn)品都進行修改的)。
然后,我們來修改一下解決并發(fā)的方式:
cc.Resolve(RefreshMode.KeepCurrentValues); // 放棄原先更新,所有更新以當(dāng)前更新為準
來測試一下,假設(shè)現(xiàn)在產(chǎn)品價格和庫存分別是27和32。那么,我們啟動程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)這里設(shè)置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為28和30了,繼續(xù)程序可以發(fā)現(xiàn)價格和庫存分別是27和31。產(chǎn)品價格沒有變化,庫存-1了,都是我們程序的功勞,SQL語句的更新被放棄了。
然后,我們再來修改一下解決并發(fā)的方式:
cc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,沖突字段以當(dāng)前更新為準
來測試一下,假設(shè)現(xiàn)在產(chǎn)品價格和庫存分別是27和32。那么,我們啟動程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)這里設(shè)置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為28和30了,繼續(xù)程序可以發(fā)現(xiàn)價格和庫存分別是28和31。這就是默認方式,在保持原先更新的基礎(chǔ)上,對于發(fā)生沖突的字段以最后更新為準。
我們甚至還可以針對不同的字段進行不同的處理策略:
foreach (ObjectChangeConflict cc in ctx.ChangeConflicts)
{
Product p = (Product)cc.Object;
foreach (MemberChangeConflict mc in cc.MemberConflicts)
{
string currVal = mc.CurrentValue.ToString();
string origVal = mc.OriginalValue.ToString();
string databaseVal = mc.DatabaseValue.ToString();
MemberInfo mi = mc.Member;
string memberName = mi.Name;
Response.Write(p.ProductID + " " + mi.Name + " " + currVal + " " + origVal +" "+ databaseVal + "<br/>");
if (memberName == "UnitsInStock")
mc.Resolve(RefreshMode.KeepCurrentValues); // 放棄原先更新,所有更新以當(dāng)前更新為準
else if (memberName == "UnitPrice")
mc.Resolve(RefreshMode.OverwriteCurrentValues); // 放棄當(dāng)前更新,所有更新以原先更新為準
else
mc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,沖突字段以當(dāng)前更新為準
}
}
比如上述代碼就對庫存字段作放棄原先更新處理,對價格字段作放棄當(dāng)前更新處理。我們來測試一下,假設(shè)現(xiàn)在產(chǎn)品價格和庫存分別是27和32。那么,我們啟動程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)這里設(shè)置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為28和30了,繼續(xù)程序可以發(fā)現(xiàn)價格和庫存分別為28和31了。說明對價格的處理確實保留了原先的更新,對庫存的處理保留了當(dāng)前的更新。頁面上顯示的結(jié)果如下圖:
最后,我們把提交語句修改為:
ctx.SubmitChanges(ConflictMode.FailOnFirstConflict);
表示第一次發(fā)生沖突的時候就不再繼續(xù)了,然后并且去除最后的ctx.SubmitChanges();語句。來測試一下,在執(zhí)行了SQL后再繼續(xù)程序可以發(fā)現(xiàn)界面上只輸出了數(shù)字1,說明在第一條記錄失敗后,后續(xù)的并發(fā)沖突就不再處理了。
事務(wù)處理
Linq to sql在提交更新的時候默認會創(chuàng)建事務(wù),一部分修改發(fā)生錯誤的話其它修改也不會生效:
ctx.Customers.Add(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });
ctx.Customers.Add(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });
ctx.SubmitChanges();
假設(shè)數(shù)據(jù)庫中已經(jīng)存在顧客ID為“abcde”的記錄,那么第二次插入操作失敗將會導(dǎo)致第一次的插入操作失效。執(zhí)行程序后會得到一個異常,查詢數(shù)據(jù)庫發(fā)現(xiàn)“abcdf”這個顧客也沒有插入到數(shù)據(jù)庫中。
如果每次更新后直接提交修改,那么我們可以使用下面的方式做事務(wù):
if (ctx.Connection != null) ctx.Connection.Open();
DbTransaction tran = ctx.Connection.BeginTransaction();
ctx.Transaction = tran;
try
{
CreateCustomer(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });
CreateCustomer(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });
tran.Commit();
}
catch
{
tran.Rollback();
}
private void CreateCustomer(Customer c)
{
ctx.Customers.Add(c);
ctx.SubmitChanges();
}
運行程序后發(fā)現(xiàn)增加顧客abcdf的操作并沒有成功?;蛘撸覀冞€可以通過TransactionScope實現(xiàn)事務(wù):
using (TransactionScope scope = new TransactionScope())
{
CreateCustomer(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });
CreateCustomer(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });
scope.Complete();
}
0
0
0(請您對文章做出評價)
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Linq to SQL 的更新沖突與管理 - 陀螺的日志 - 網(wǎng)易博客
SQL 2000中的觸發(fā)器使用
Linq to SQL: Delete時遇到的問題
精妙sql
第八節(jié) SQL語句 4
sql語句
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服