關於DataContext Attach()的眉眉角角

上回提到LINQ to SQL兩段式更新時,經網友提醒有Attach()這個好東西,今天試了一下,結果發現它並不是我所原本想像的好東西,用起來得費一些手腳。

MVP Rick Strahl對這個議題有兩篇文章(1, 2)做了深入探討,因此細節我就不再贅述,但簡單歸納一下我的整理:

Table(TEntity).Attach()有三個Overloading:

  1. 若只使用Attach(entity),不會產生任何SQL的更新動作。
  2. 使用Attach(entity, asModified),當設為true,但entity沒有Timestamp欄位時,會得到 An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy. 錯誤。
  3. 先建立DataContext,使用Single(...)查出資料庫中的現有版本放入origEntity,再使用Attach(entity, origEntity),會得到Cannot add an entity with a key that is already in use.錯誤。

如果你不想為了Attach在資料表多加一個Timestamp欄位,也不想手動修改DBML加入UpdateCheck.Never屬性,那就只能由從第三種方法下手。

解決方式是"用另一個DataContext查出origEntity"(或者用序列化程序將origEntity"漂白"成與DataContext無關,但感覺上更繞路),如以下示範:

        Member orig = null;
        //要用不同的DataContext先取出資料物件,稍後做比對用
        using (PlaygroundDataContext db = new PlaygroundDataContext())
        {
            orig = db.Members
                .Single(o => o.UserId == int.Parse(e.DataKeyString));
        }
        //更新要用另一個DataContext
        using (PlaygroundDataContext db = new PlaygroundDataContext())
        {
            var de = new Member();
            //將HTML編修的結果更新到資料物件
              UpdateHelper.SyncData(de);
            db.Members.Attach(de, orig);
            db.SubmitChanges();
        }

PS: 因為Attach()種種限制,在前述文章討論中也有一些人是採用兩段式更新文中用的Refection重設資料值做法。

[2010-07-02更新]網友提供了更簡便的做法如下,感謝SuperShowwei分享!

        using (PlaygroundDataContext db = new PlaygroundDataContext())
        {
            db.Log = new DebuggerWriter();
            var de = new Member();
            //將HTML編修的結果更新到資料物件
              UpdateHelper.SyncData(de);
            db.Members.Attach(de);
            db.Refresh(System.Data.Linq.RefreshMode.KeepCurrentValues, de);
            db.SubmitChanges();
        }
歡迎推文分享:
Published 25 June 2010 02:47 PM 由 Jeffrey
Filed under: , ,
Views: 13,826



意見

# SuperShowwei said on 02 July, 2010 04:25 AM

我都是這樣寫:

DB.Table.Attach(Orig);

DB.Table.Refresh(RefreshMode.KeepCurrentValues, Orig);

DB.SubmitChanges();

# Jeffrey said on 02 July, 2010 05:43 AM

to SuperShowwei, 真是簡便的好方法,謝謝分享!! 已加入本文中。

# psplay said on 08 July, 2010 02:21 AM

請問一下 UpdateHelper.SyncData(de); 這一段的 function 在哪裡??

它的功能是做什麼的呢??

謝謝...

另外這樣使用 Update 的方式好像很麻煩說...

用來用去還是 Entity Framework 比較方便一些....

# Jeffrey said on 08 July, 2010 03:52 AM

to psplay,  UpdateHelper.SyncData(de)是我虛擬的一個函數,你可以想像成它的功用為: de.UserName = txtUserName.Text; de.Code = txtCode.Text;

# psplay said on 08 July, 2010 06:56 PM

reply to Jeffrey

原來是這樣...

但是這樣感覺順序有點怪怪的,因為將 HTML 上控制項的值設定到 LINQ TO SQL 的 Entity 不是應該在 aspx 頁面中的事件去設定嗎??

進到資料存取層的時候再去呼叫那個 function 要怎麼抓到頁面上的值呢??

好像有點離題了...只是有點好奇怎麼辦到的...

# wow said on 30 August, 2010 10:01 PM

DB.Table.Attach(Orig);

DB.Table.Refresh(RefreshMode.KeepCurrentValues, Orig);

DB.SubmitChanges();

------------------------------------------------------------

Orig是原始数据?那update的内容怎么更新到数据库的呢?

# Jeffrey said on 30 August, 2010 10:52 PM

to wow, 範例中省略了更新de屬性值的細節(假設de.PropertyA="ABC"之類的屬性變更邏輯在UpdateHelper.SyncData(de)裡被完成):

//將HTML編修的結果更新到資料物件

UpdateHelper.SyncData(de); <-- 在這裡動手腳

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<June 2010>
SunMonTueWedThuFriSat
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication