我喜歡LINQ to SQL的簡潔,就拿更新資料庫某筆資料這件事來說,你可以忘記SqlConnection、丟掉SqlCommand、抛下SqlParameter,就搞定整個更新動作,對寫慣ADO.NET的人來說,實在是件不可思議的事。

像下面這個例子,寫一段LINQ配上Single()取得資料物件,重新指定值,然後SubmitChanges()就完成了ID=2 Player資料的CreateTime欄位更新。有種袖子都還沒捲起來,敵人就忽然自已暴斃的莫名爽快。

    protected void Page_Load(object sender, EventArgs e)
    {
        PlaygroundDataClassesDataContext db = 
            new PlaygroundDataClassesDataContext();
        db.Log = new DebuggerWriter();
        (from o in db.Players where o.ID == 2 select o)
            .Single().CreateTime = DateTime.Now;
        db.SubmitChanges();
    }

上回既然提過DebuggerWriter,我們就不該只知其然不知其所以然,來看看背後發生什麼事吧!

這裡發生了兩個SQL動作,在Single()時會SELECT取回ID=2的資料,而SubmitChages()則進行UPDATE動作。值得一提的是,在UPDATE時,除了ID=2,它還會一併比較Name及CreateTime,用意是確認資料讀出到更新這段期間資料沒有被其他人更動過,防止多人同時更新資料庫時發生彼此資料互相覆寫衝突。

要印證它的保護效果,我們可以在db.SubmitChanges()上設定中斷點,讓VS2008跑偵錯模式停在該中斷點上,此時另外開SQL Server Management Studio執行T-SQL更新ID=2的資料:

UPDATE Player SET CreateTime = '2009-07-08 12:34:56'
WHERE ID = 2

接著好戲上場,在VS2008按下F10執行db.SubmitChanges(),會發生以下例外:

System.Data.Linq.ChangeConflictException: Row not found or changed. (中文版為: 資料列找不到,或者已變更。)

這個實驗證實了LINQ to SQL的確有提供資料庫更新時的衝突管理,然而要如何妥善處理更新衝突是門學問,保哥有篇文章做了進一步的剖析,推薦給大家參考。

當然,如果效能不是最優先考量,我還有一招。以前介紹過可在LINQ to SQL上使用的TransactionScope大法,在Single()到SubmitChanges()間對資料上鎖,防止別人擅動,就不會有衝突的問題(但想其他要更新資料的人會被卡住,直到這一方的UPDATE完成為止)。在此還是要善盡提醒之責,包TransactionScope的方法較適合更新頻率不高、鎖定時間不長、使用人數不多的情境,採用前宜審慎評估,不然有可能大幅拖累系統整體效能。

    protected void Page_Load(object sender, EventArgs e)
    {
        using (System.Transactions.TransactionScope tx
            = new System.Transactions.TransactionScope())
        {
            PlaygroundDataClassesDataContext db =
                new PlaygroundDataClassesDataContext();
            db.Log = new DebuggerWriter();
            (from o in db.Players where o.ID == 2 select o)
                .Single().CreateTime = DateTime.Now;
            db.SubmitChanges();
        }
    }

Comments

Be the first to post a comment

Post a comment


96 - 9 =