上回提到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();
        }

Comments

# by SuperShowwei

我都是這樣寫: DB.Table.Attach(Orig); DB.Table.Refresh(RefreshMode.KeepCurrentValues, Orig); DB.SubmitChanges();

# by Jeffrey

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

# by psplay

請問一下 UpdateHelper.SyncData(de); 這一段的 function 在哪裡?? 它的功能是做什麼的呢?? 謝謝... 另外這樣使用 Update 的方式好像很麻煩說... 用來用去還是 Entity Framework 比較方便一些....

# by Jeffrey

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

# by psplay

reply to Jeffrey 原來是這樣... 但是這樣感覺順序有點怪怪的,因為將 HTML 上控制項的值設定到 LINQ TO SQL 的 Entity 不是應該在 aspx 頁面中的事件去設定嗎?? 進到資料存取層的時候再去呼叫那個 function 要怎麼抓到頁面上的值呢?? 好像有點離題了...只是有點好奇怎麼辦到的...

# by wow

DB.Table.Attach(Orig); DB.Table.Refresh(RefreshMode.KeepCurrentValues, Orig); DB.SubmitChanges(); ------------------------------------------------------------ Orig是原始数据?那update的内容怎么更新到数据库的呢?

# by Jeffrey

to wow, 範例中省略了更新de屬性值的細節(假設de.PropertyA="ABC"之類的屬性變更邏輯在UpdateHelper.SyncData(de)裡被完成): //將HTML編修的結果更新到資料物件 UpdateHelper.SyncData(de); <-- 在這裡動手腳

Post a comment