昨天遇到一段棘手的程式,嘗試了各種方法,忽而在SubmitChanges的時候沒反應(無錯誤,也不更新),忽而發生ChangeConflict,經過幾個小時,終于大緻理清了思路,也順便把DataContext/UpdateModel/SubmitChanges給搞得更明白了一些,特此分享。
先大緻看看代碼:
xxController
{
AgileRepository _repAgile = new AgileRepository(); //這裡邊是SubmitChanges/DateContext/Tables等屬性,可取出下面提到的story
SFCRepository _repSFC = new SFCRepository(); //相同,可取出下面提到的UDCs
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
Story story = _repAgile.GetStoryAt(id);
try
UpdateModel(story);
foreach (var udc in story.UDCs)
UpdateModel(udc, udc.ID.ToString("D6"));
}
_repAgile.Save();
_repSFC.Save(); //這裡也是
...
之是以出現紅色的_repSFC.Save(),是因為story的一個屬性List<IUDC> UDCs,也是需要在這個頁面被更新的内容(在View中有控件與其對應),而它的Get過程最初是:
class Story
public List<IUDC> UDCs
{
get
SFCRepository rep = new SFCRepository();
return rep.GetUDCs().Where(...);
}
set { }
這裡藍色的rep和前面紅色的rep不是同一個,是以如果從藍色rep的當中取出UDCs并進行UpdateModel,而對另外一個無關的紅色rep儲存,什麼也不會發生;而如果兩者都取出UDCs并都曾經被UpdateModel,在Save(内部執行了SubmitChanges)的時候,會報出confilict changes exception,這個Exception極其麻煩而且不透明Google/MSDN上能找到一些資料但很多不工作。
其實,全部解決方法的秘訣其實就是:讓取出資料的那個Repository執行Save操作(或者從内部看,是讓取出資料的DataContext執行SubmitChanges操作)。
藍色rep的是個局部變量,活不到Save的時候了,隻能用紅色的那個rep了。代碼改為下面這個就好了:
SFCRepository _repSFC = new SFCRepository(); //下面取資料/儲存的都是它。
_repSFC.GetUDCsFor(story); //在這裡邊讓_repSFC的DataContext取資料。
_repSFC.Save(); //這裡會完成存儲。
盡管能用了,但這段代碼很不好,尤其GetUDCsFor,調用的位置很生硬,不好讀也很容易出錯。
最終還是這樣最好:
外面:
UpdateModel(udc, udc.ID.ToString("D6")); //Update的
story.SaveUDCs(); // 這個調用看着順眼。
裡邊:
public partial class Story : IUDCable, IItem
private SFCRepository _repSFC = new SFCRepository(); //這個方案裡取資料/儲存的都是它。_repSFC不再是個局部變量,生命周期正好和UDCs相同。
public void SaveUDCs()
_repSFC.Save(); // 在這裡儲存
_repSFC.GetUDCOf(this, ref _udcs); //取資料。
return _udcs;
後面本來不應該再把_udcs傳入GetUDCOf了,直接但因為别的IUDCable也要用到,是以封裝了一個函數。
這個方案,取資料/存資料的都是Story内部的_repSFC,而且封裝性更好,是最終結果。
最後重複一下在這種場景中發生問題時的解決方法的秘訣其實就是:讓取出資料的那個Repository執行Save操作(或者從内部看,是讓取出資料的DataContext執行SubmitChanges操作)。
本文轉自火星人陳勇 51CTO部落格,原文連結:http://blog.51cto.com/cheny/1100087