天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

一步一步學Linq to sql(七):并發與事務

系列文章導航:

一步一步學Linq to sql(一):預備知識

一步一步學Linq to sql(二):DataContext與實體

一步一步學Linq to sql(三):增刪改

一步一步學Linq to sql(四):查詢句法

一步一步學Linq to sql(五):存儲過程

一步一步學Linq to sql(六):探究特性

一步一步學Linq to sql(七):并發與事務

一步一步學Linq to sql(八):繼承與關系

一步一步學Linq to sql(九):其它補充

一步一步學Linq to sql(十):分層構架的例子


檢測并發

       首先使用下面的SQL語句查詢數據庫的產品表:

select * from products where categoryid=1

       查詢結果如下圖:

 

       為了看起來清晰,我已經事先把所有分類為1產品的價格和庫存修改為相同值了。然后執行下面的程序:

        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(); // 在這里設斷點

       我們使用調試方式啟動,由于設置了斷點,程序并沒有進行更新操作。此時,我們在數據庫中運行下面的語句:

update products

set unitsinstock = unitsinstock -2, unitprice= unitprice + 1

where categoryid = 1

       然后在繼續程序,會得到修改并發(樂觀并發沖突)的異常,提示要修改的行不存在或者已經被改動。當客戶端提交的修改對象自讀取之后已經在數據庫中發生改動,就產生了修改并發。解決并發的包括兩步,一是查明哪些對象發生并發,二是解決并發。如果你僅僅是希望更新時不考慮并發的話可以關閉相關列的更新驗證,這樣在這些列上發生并發就不會出現異常:

[Column(Storage="_UnitsInStock", DbType="SmallInt", UpdateCheck = UpdateCheck.Never)]

[Column(Storage="_UnitPrice", DbType="Money", UpdateCheck = UpdateCheck.Never)]

       為這兩列標注不需要進行更新檢測。假設現在產品價格和庫存分別是2732。那么,我們啟動程序(設置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為2830了,繼續程序可以發現價格和庫存分別是2831。價格+1是之前更新的功勞,庫存最終是-1是我們程序之后更新的功勞。當在同一個字段上(庫存)發生并發沖突的時候,默認是最后的那次更新獲勝。

系列文章導航:

一步一步學Linq to sql(一):預備知識

一步一步學Linq to sql(二):DataContext與實體

一步一步學Linq to sql(三):增刪改

一步一步學Linq to sql(四):查詢句法

一步一步學Linq to sql(五):存儲過程

一步一步學Linq to sql(六):探究特性

一步一步學Linq to sql(七):并發與事務

一步一步學Linq to sql(八):繼承與關系

一步一步學Linq to sql(九):其它補充

一步一步學Linq to sql(十):分層構架的例子


解決并發

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

        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); // 放棄當前更新,所有更新以原先更新為準

            }

        }

        ctx.SubmitChanges();

       首先可以看到,我們使用try{}catch{}來捕捉并發沖突的異常。在SubmitChanges的時候,我們選擇了ConflictMode.ContinueOnConflict選項。也就是說遇到并發了還是繼續。在catch{}中,我們從ChangeConflicts中獲取了并發的對象,然后經過類型轉化后輸出了產品ID,然后選擇的解決方案是RefreshMode.OverwriteCurrentValues。也就是說,放棄當前的更新,所有更新以原先更新為準。

       我們來測試一下,假設現在產品價格和庫存分別是2732。那么,我們啟動程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)這里設置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為2830了,繼續程序可以發現價格和庫存分別是2830。之前SQL語句庫存-2生效了,而我們程序的更新(庫存-1)被放棄了。在頁面上也顯示了所有分類為1的產品ID(因為我們之前的SQL語句是對所有分類為1的產品都進行修改的)。

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

cc.Resolve(RefreshMode.KeepCurrentValues); // 放棄原先更新,所有更新以當前更新為準

       來測試一下,假設現在產品價格和庫存分別是2732。那么,我們啟動程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)這里設置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為2830了,繼續程序可以發現價格和庫存分別是2731。產品價格沒有變化,庫存-1了,都是我們程序的功勞,SQL語句的更新被放棄了。

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

cc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,沖突字段以當前更新為準

       來測試一下,假設現在產品價格和庫存分別是2732。那么,我們啟動程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)這里設置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為2830了,繼續程序可以發現價格和庫存分別是2831。這就是默認方式,在保持原先更新的基礎上,對于發生沖突的字段以最后更新為準。

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

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); // 放棄原先更新,所有更新以當前更新為準

        else if (memberName == "UnitPrice")

            mc.Resolve(RefreshMode.OverwriteCurrentValues); // 放棄當前更新,所有更新以原先更新為準

        else

            mc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,沖突字段以當前更新為準

 

    }

}

       比如上述代碼就對庫存字段作放棄原先更新處理,對價格字段作放棄當前更新處理。我們來測試一下,假設現在產品價格和庫存分別是2732。那么,我們啟動程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)這里設置端點),然后運行UPDATE語句,把價格+1,庫存-2,然后價格和庫存分別為2830了,繼續程序可以發現價格和庫存分別為2831了。說明對價格的處理確實保留了原先的更新,對庫存的處理保留了當前的更新。頁面上顯示的結果如下圖:

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

ctx.SubmitChanges(ConflictMode.FailOnFirstConflict);

       表示第一次發生沖突的時候就不再繼續了,然后并且去除最后的ctx.SubmitChanges();語句。來測試一下,在執行了SQL后再繼續程序可以發現界面上只輸出了數字1,說明在第一條記錄失敗后,后續的并發沖突就不再處理了。

系列文章導航:

一步一步學Linq to sql(一):預備知識

一步一步學Linq to sql(二):DataContext與實體

一步一步學Linq to sql(三):增刪改

一步一步學Linq to sql(四):查詢句法

一步一步學Linq to sql(五):存儲過程

一步一步學Linq to sql(六):探究特性

一步一步學Linq to sql(七):并發與事務

一步一步學Linq to sql(八):繼承與關系

一步一步學Linq to sql(九):其它補充

一步一步學Linq to sql(十):分層構架的例子


事務處理

       Linq to sql在提交更新的時候默認會創建事務,一部分修改發生錯誤的話其它修改也不會生效:

        ctx.Customers.Add(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });

        ctx.Customers.Add(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });

        ctx.SubmitChanges();

       假設數據庫中已經存在顧客ID為“abcde”的記錄,那么第二次插入操作失敗將會導致第一次的插入操作失效。執行程序后會得到一個異常,查詢數據庫發現“abcdf”這個顧客也沒有插入到數據庫中。

       如果每次更新后直接提交修改,那么我們可以使用下面的方式做事務:

        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();

    }

       運行程序后發現增加顧客abcdf的操作并沒有成功。或者,我們還可以通過TransactionScope實現事務:

        using (TransactionScope scope = new TransactionScope())

        {

            CreateCustomer(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });

            CreateCustomer(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });

            scope.Complete();

        }

      

it知識庫一步一步學Linq to sql(七):并發與事務,轉載需保留來源!

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

主站蜘蛛池模板: 年轻的母亲4线在线观看完整 | 精品AV亚洲乱码一区二区 | 日本特交大片免费观看 | 亚洲 日本 欧美 中文字幕 | 国产91青青成人a在线 | 日产2021免费一二三四区在线 | 午夜伦yy44880影院 | 超碰97人人做人人爱亚洲尤物 | 黑色丝袜在线观看 | 国产午夜精品视频在线播放 | 亚瑟天堂久久一区二区影院 | 中文字幕AV亚洲精品影视 | 精品国产人成亚洲区 | 国产在线一卡二卡 | 精品久久久亚洲精品中文字幕 | 久久久无码精品亚洲欧美 | 人妻无码AV中文系统久久免费 | 寂寞夜晚看免费视频 | 亚洲蜜桃AV永久无码精品放毛片 | 亚洲欧洲日产国产 最新 | 欧美乱妇日本无乱码特黄大片 | MATURETUBE乱妇| 国产亚洲精品久久播放 | 一区二区三区无码被窝影院 | 2021国产精品 | 中国大陆一级毛片免费 | 色久久综合视频本道88 | 99精品热视频30在线热视频 | 把她带到密室调教性奴 | 亚洲春色AV无码专区456 | 中文字幕无码亚洲字幕成A人蜜桃 | 熟女少妇内射日韩亚洲 | 亚洲精品乱码电影在线观看 | 欧美精品AV精品一区视频 | 娇小萝被两个黑人用半米长 | 男生J桶进女人P又色又爽又黄 | 免费夜里18款禁用软粉色 | 亚洲无人区码二码三码区别图 | 国产亚洲精品视频在线网 | 女人被躁到高潮嗷嗷叫69 | 色多多污污下载 |