關於DataContext Attach()的眉眉角角
7 |
上回提到LINQ to SQL兩段式更新時,經網友提醒有Attach()這個好東西,今天試了一下,結果發現它並不是我所原本想像的好東西,用起來得費一些手腳。
MVP Rick Strahl對這個議題有兩篇文章(1, 2)做了深入探討,因此細節我就不再贅述,但簡單歸納一下我的整理:
Table(TEntity).Attach()有三個Overloading:
- 若只使用Attach(entity),不會產生任何SQL的更新動作。
- 使用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. 錯誤。
- 先建立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); <-- 在這裡動手腳