從一個案例看MVC中DataContext和UpdateModel的工作原理(詳解UpdateModel/SubmitChanges錯誤)

來源:互聯網
上載者:User

昨天遇到一段棘手的程式,嘗試了各種方法,忽而在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了。代碼改為下面這個就好了:

xxController

{

 SFCRepository _repSFC = new SFCRepository(); //下面取資料/儲存的都是它。

 

[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
    Story story = _repAgile.GetStoryAt(id);

    _repSFC.GetUDCsFor(story); //在這裡邊讓_repSFC的DataContext取資料。

    try
    {
        UpdateModel(story);
        foreach (var udc in story.UDCs)
        {
             UpdateModel(udc, udc.ID.ToString("D6"));
        }
        _repAgile.Save();

        _repSFC.Save(); //這裡會完成儲存。
        ...
}

儘管能用了,但這段代碼很不好,尤其GetUDCsFor,調用的位置很生硬,不好讀也很容易出錯。

最終還是這樣最好:

 

外面:

            Story story = _repAgile.GetStoryAt(id);

            try
            {
                UpdateModel(story);
                foreach (var udc in story.UDCs)
                {
                    UpdateModel(udc, udc.ID.ToString("D6")); //Update的
                }
                story.SaveUDCs(); // 這個調用看著順眼。
                _repAgile.Save();
                ...
            }
裡邊:

   public partial class Story : IUDCable, IItem
    {

        private SFCRepository _repSFC = new SFCRepository(); //這個方案裡取資料/儲存的都是它。_repSFC不再是個局部變數,生命週期正好和UDCs相同。
        public void SaveUDCs()
        {
            _repSFC.Save(); // 在這裡儲存
        }
        public List<IUDC> UDCs 
        {
            get
            {
                _repSFC.GetUDCOf(this, ref _udcs); //取資料。
                return _udcs;
            } 
        }
後面本來不應該再把_udcs傳入GetUDCOf了,直接但因為別的IUDCable也要用到,所以封裝了一個函數。

這個方案,取資料/存資料的都是Story內部的_repSFC,而且封裝性更好,是最終結果。

 

最後重複一下在這種情境中發生問題時的解決方案的秘訣其實就是:讓取出資料的那個Repository執行Save操作(或者從內部看,是讓取出資料的DataContext執行SubmitChanges操作)

 

點擊下載免費的敏捷開發教材:《火星人敏捷開發手冊》

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.