LINQ 會怎麼做?和上面一樣!取出所有欄位,把 View 加一,用所有欄位作為條件 (包含 Views) 更新回去。
設想一個被頻繁使用的計數器,兩次操作出現 SELECT 與 UPDATE 交叉情況可能性很大,那麼後者還能更新成功嗎?
微軟解釋:如果在你更新過程中,有其他人更新了這一列,那麼這一列也就不是你所需的那一列了,為了防止這樣的衝突,所以把所有欄位都放在 WHERE 陳述式中,這是 by design 的。
你可以透過其他方法更新資料,然而在目前版本,這個方法表現也不怎麼樣。
System.Data.Linq.Table<T> 有一個 Attach 方法,帶有三個多載,用來直接更新資料,我們逐一來看。
var ctx = new MyDataContext();
var newUser = new User();
newUser.UserId = new Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"); // 假設作為參數傳進來
newUser.UserName = "New User Name";
ctx.Users.Attach(newUser);
ctx.SubmitChanges();
執行完全沒有任何效果,SQL Profiler 無任何記錄。
var ctx = new MyDataContext();
var newUser = new User();
newUser.UserId = new Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
newUser.UserName = "New User Name";
var user = ctx.User.Where(u => u.UserId == newUser.UserId).Single();
ctx.Users.Attach(newUser, user);
ctx.SubmitChanges();
執行時提示:Cannot add an entity with a key that is already in use.
var ctx = new MyDataContext();
var newUser = new User();
newUser.UserId = new Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
newUser.UserName = "New User Name";
ctx.Users.Attach(newUser, true);
ctx.SubmitChanges();
執行時提示: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.
怎麼辦?提示說 "declares a version member",通常指 SQL Server 中 TimeStamp 型別欄位,在需更新的表中加上一個欄位並標記為 TimeStamp 就可以了。但這樣做對我們仍是浪費,且 WHERE 子句仍會出現 TimeStamp 限制。
也可以在欄位上設定 UpdateCheck.Never 屬性避免更新檢查,但若資料表更新、新增預存程序需重新產生 dbml,你得手動重新設定一遍。
LINQ 甚至沒有類似 Web 參考中 "Update Web Reference" 的操作讓你方便在資料表更新後更新 dbml,這個版本也不會提供,你只能刪除原來的表、重新整理 Server Explorer、重新拖曳到 dbml 設計檢視,或寫腳本讓 SQLMetal 幫你完成。
LINQ 雖作為查詢語言出現,但在資料更新方面也做了不少工作,特別是一些 CHECK 工作。但對寫慣 SQL 的我們來說,仍很不習慣,甚至覺得這些工作你不替我做才好。
在沒有更好解決辦法前提下,更新操作上,老老實實寫 SQL 陳述式或預存程序應該是不錯選擇。