2012-09-28 83 views
1

我有這樣的兩個對象 - 雜誌和作者(MM關係):編輯的模式 - 複雜的類型不正確更新

public partial class MAGAZINE 
    { 
     public MAGAZINE() 
     { 
      this.AUTHORs = new HashSet<AUTHOR>(); 
     } 

     public long REF_ID { get; set; } 
     public string NOTES { get; set; } 
     public string TITLE { get; set; } 

     public virtual REFERENCE REFERENCE { get; set; } 
     public virtual ICollection<AUTHOR> AUTHORs { get; set; } 
    } 

public partial class AUTHOR 
{ 
    public AUTHOR() 
    { 
     this.MAGAZINEs = new HashSet<MAGAZINE>(); 
    } 

      public long AUTHOR_ID { get; set; } 
      public string FULL_NAME { get; set; } 

      public virtual ICollection<MAGAZINE> MAGAZINEs { get; set; } 
     } 
} 

我的問題是,我似乎無法更新對一個作家的數量雜誌例如如果我有一位作者叫「Smith,P.」我可以添加另一個名爲「Jones,D.」的文檔,但是在發回到編輯控制器後,作者的數量仍然顯示爲1 - 即「Smith,P.H」。

請不要說我已經成功地將作者數量綁定到父實體(雜誌),它使用自定義模型綁定器來檢索作者並綁定到雜誌(我認爲),但它仍然沒有「 t似乎正確更新。

我的更新型號代碼是直線前進 - 和之前和之後顯示變量值:

public ActionResult Edit(long id) 
    { 
     MAGAZINE magazine = db.MAGAZINEs.Find(id); 
     return View(magazine); 
    } 

這裏是變量預編輯/更新 -

enter image description here

[HttpPost] 
public ActionResult Edit(MAGAZINE magazine) 
    { 
     if (ModelState.IsValid) 
     { 
      db.Entry(magazine).State = EntityState.Modified; 
      db.SaveChanges(); 
      return RedirectToAction("Index"); 
     } 

     return View(magazine); 
    } 

...這裏是一個新的作者之後的變量已被添加...

我越來越懷疑作者實體顯示,發佈編輯,它沒有綁定到任何雜誌,我猜這就是爲什麼它沒有被更新回雜誌實體 - 但這是令人困惑的,因爲我正在有效地處理同一雜誌實體 - 我想這可能與作者的自定義模型聯編程序有關。

任何人都可以幫忙嗎?

爲了完整性 - 我已經包括了我的AuthorModelBinder類太 -

public class AuthorModelBinder : IModelBinder 
    { 
     public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
     { 
      var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
      if (values != null) 
      { 
       // We have specified asterisk (*) as a token delimiter. So 
       // the ids will be separated by *. For example "2*3*5" 
       var ids = values.AttemptedValue.Split('*'); 

       List<int> validIds = new List<int>(); 
       foreach (string id in ids) 
       { 
        int successInt; 
        if (int.TryParse(id, out successInt)) 
        { 
         validIds.Add(successInt); 
        } 
        else 
        { 
         //Make a new author 
         AUTHOR author = new AUTHOR(); 
         author.FULL_NAME = id.Replace("\'", "").Trim(); 
         using (RefmanEntities db = new RefmanEntities()) 
         { 
          db.AUTHORs.Add(author); 
          db.SaveChanges(); 
          validIds.Add((int)author.AUTHOR_ID); 
         } 
        } 
       } 

       //Now that we have the selected ids we could fetch the corresponding 
       //authors from our datasource 
       var authors = AuthorController.GetAllAuthors().Where(x => validIds.Contains((int)x.Key)).Select(x => new AUTHOR 
       { 
        AUTHOR_ID = x.Key, 
        FULL_NAME = x.Value 
       }).ToList(); 
       return authors; 
      } 
      return Enumerable.Empty<AUTHOR>(); 
     } 
    } 

enter image description here

回答

2

當我使用MVC/Nhibernate開發我的博客並且實體是PostTag時,我遇到了一個非常類似的情況。

我也曾經有過的編輯操作這樣的事情,

public ActionResult Edit(Post post) 
{ 
    if (ModelState.IsValid) 
    { 
     repo.EditPost(post); 
     ... 
    } 
    ... 
} 

但不像你,我已經創建了Post沒有Tag自定義模型粘合劑。在自定義PostModelBinder我正在做與你在那裏做的幾乎相同的事情(但我沒有創建新的Tag,就像你在爲Author一樣)。基本上,我創建了一個新的Post實例,其中包含來自POST表單的所有屬性,並從數據庫中獲取所有Tag的ID。請注意,我只從數據庫中取得Tag而不是Post

我可能會建議你爲Magazine創建一個ModelBinder並檢查它。此外,最好使用存儲庫模式,而不是直接從控制器進行調用。

UPDATE:

這裏是Post模型綁定的完整的源代碼

namespace PrideParrot.Web.Controllers.ModelBinders 
{ 
    [ValidateInput(false)] 
    public class PostBinder : IModelBinder 
    { 
    private IRepository repo; 

    public PostBinder(IRepository repo) 
    { 
     this.repo = repo; 
    } 

    #region IModelBinder Members 

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     HttpRequestBase request = controllerContext.HttpContext.Request; 

     // retrieving the posted values. 
     string oper = request.Form.Get("oper"), 
       idStr = request.Form.Get("Id"), 
       heading = request.Form.Get("Heading"), 
       description = request.Form.Get("Description"), 
       tagsStr = request.Form.Get("Tags"), 
       postTypeIdStr = request.Form.Get("PostType"), 
       postedDateStr = request.Form.Get("PostedDate"), 
       isPublishedStr = request.Form.Get("Published"), 
       fileName = request.Form.Get("FileName"), 
       serialNoStr = request.Form.Get("SerialNo"), 
       metaTags = request.Form.Get("MetaTags"), 
       metaDescription = request.Form.Get("MetaDescription"), 
       themeIdStr = request.Form.Get("Theme"); 

     // initializing to default values. 
     int id = 0, serialNo = 0; 
     DateTime postedDate = DateTime.UtcNow; 
     DateTime? modifiedDate = DateTime.UtcNow; 
     postedDate.AddMilliseconds(-postedDate.Millisecond); 
     modifiedDate.Value.AddMilliseconds(-modifiedDate.Value.Millisecond); 

     /*if operation is not specified throw exception. 
     operation should be either'add' or 'edit'*/ 
     if (string.IsNullOrEmpty(oper)) 
     throw new Exception("Operation not specified"); 

     // if there is no 'id' in edit operation add error to model. 
     if (string.IsNullOrEmpty(idStr) || idStr.Equals("_empty")) 
     { 
     if (oper.Equals("edit")) 
      bindingContext.ModelState.AddModelError("Id", "Id is empty"); 
     } 
     else 
     id = int.Parse(idStr); 

     // check if heading is not empty. 
     if (string.IsNullOrEmpty(heading)) 
     bindingContext.ModelState.AddModelError("Heading", "Heading: Field is required"); 
     else if (heading.Length > 500) 
     bindingContext.ModelState.AddModelError("HeadingLength", "Heading: Length should not be greater than 500 characters"); 

     // check if description is not empty. 
     if (string.IsNullOrEmpty(description)) 
     bindingContext.ModelState.AddModelError("Description", "Description: Field is required"); 

     // check if tags is not empty. 
     if (string.IsNullOrEmpty(metaTags)) 
     bindingContext.ModelState.AddModelError("Tags", "Tags: Field is required"); 
     else if (metaTags.Length > 500) 
     bindingContext.ModelState.AddModelError("TagsLength", "Tags: Length should not be greater than 500 characters"); 

     // check if metadescription is not empty. 
     if (string.IsNullOrEmpty(metaTags)) 
     bindingContext.ModelState.AddModelError("MetaDescription", "Meta Description: Field is required"); 
     else if (metaTags.Length > 500) 
     bindingContext.ModelState.AddModelError("MetaDescription", "Meta Description: Length should not be greater than 500 characters"); 

     // check if file name is not empty. 
     if (string.IsNullOrEmpty(fileName)) 
     bindingContext.ModelState.AddModelError("FileName", "File Name: Field is required"); 
     else if (fileName.Length > 50) 
     bindingContext.ModelState.AddModelError("FileNameLength", "FileName: Length should not be greater than 50 characters"); 

     bool isPublished = !string.IsNullOrEmpty(isPublishedStr) ? Convert.ToBoolean(isPublishedStr.ToString()) : false; 

     //** TAGS 
     var tags = new List<PostTag>(); 
     var tagIds = tagsStr.Split(','); 
     foreach (var tagId in tagIds) 
     { 
     tags.Add(repo.PostTag(int.Parse(tagId))); 
     } 
     if(tags.Count == 0) 
     bindingContext.ModelState.AddModelError("Tags", "Tags: The Post should have atleast one tag"); 

     // retrieving the post type from repository. 
     int postTypeId = !string.IsNullOrEmpty(postTypeIdStr) ? int.Parse(postTypeIdStr) : 0; 
     var postType = repo.PostType(postTypeId); 
     if (postType == null) 
     bindingContext.ModelState.AddModelError("PostType", "Post Type is null"); 

     Theme theme = null; 
     if (!string.IsNullOrEmpty(themeIdStr)) 
     theme = repo.Theme(int.Parse(themeIdStr)); 

     // serial no 
     if (oper.Equals("edit")) 
     { 
     if (string.IsNullOrEmpty(serialNoStr)) 
      bindingContext.ModelState.AddModelError("SerialNo", "Serial No is empty"); 
     else 
      serialNo = int.Parse(serialNoStr); 
     } 
     else 
     { 
     serialNo = repo.TotalPosts(false) + 1; 
     } 

     // check if commented date is not empty in edit. 
     if (string.IsNullOrEmpty(postedDateStr)) 
     { 
     if (oper.Equals("edit")) 
      bindingContext.ModelState.AddModelError("PostedDate", "Posted Date is empty"); 
     } 
     else 
     postedDate = Convert.ToDateTime(postedDateStr.ToString()); 

     // CREATE NEW POST INSTANCE 
     return new Post 
     { 
     Id = id, 
     Heading = heading, 
     Description = description, 
     MetaTags = metaTags, 
     MetaDescription = metaDescription, 
     Tags = tags, 
     PostType = postType, 
     PostedDate = postedDate, 
     ModifiedDate = oper.Equals("edit") ? modifiedDate : null, 
     Published = isPublished, 
     FileName = fileName, 
     SerialNo = serialNo, 
     Theme = theme 
     }; 
    } 

    #endregion 
    } 
} 
+0

答覆!Hoorah,這是Mark的安慰,因爲自寫這篇文章後,我意識到我需要一個MagazineModelBinder類,所以我做了一個。但是當你做一個新的職位時你做什麼?你是否刪除現有的Post,並重新從頭開始重新創建?在我的情況下,我通過ID檢索雜誌,並清除了作者,然後應用我的新作家集(即使之前也存在),但是,它開始創建新的作者,並且我在Authors表中獲得重複項:你在你的聯編程序中做了什麼? – Vidar

+1

@Vidar我包含了PostBinderBinder的源代碼,你可以看看那個 – VJAI

+0

如果你還有問題,請告訴我 – VJAI

2

此行db.Entry(magazine).State = EntityState.Modified;只告訴EF該雜誌的實體發生了變化。它沒有提到關係。如果您撥打Attach對象圖中的所有實體都以Unchanged狀態附加,您必須分別處理它們中的每一個。在多對多關係you must also handle relation itself(以及關係變化狀態in DbContext API is not possible)的情況下甚至更糟糕。

我花了很多時間在這problem and design in disconnected app。有三種一般方法:

  • 您將向實體發送更多信息以查找已更改內容和已刪除內容(是的,您需要跟蹤已刪除的項目或關係)。然後您將手動設置對象圖中每個實體和關係的狀態。
  • 您只需使用您目前擁有的數據,而不是將它們附加到上下文中,您將加載當前雜誌和每個需要的作者,並在那些加載的實體上重建這些更改。
  • 你不會這樣做,而是使用輕量級的AJAX調用來添加或刪除每個作者。我發現這在很多複雜的用戶界面中都很常見。
+0

嗯,我的意思是這在最好的可能的方式 - 「我希望你是錯誤的」,因爲如果不是這樣,我想想整個EF路線都是一個巨大的錯誤,它會導致我做這麼多工作,我覺得它根本不值得! – Vidar

+0

以其他方式思考 - 如何在沒有EF的情況下做到這一點?在您的操作中接收到數據時,您仍然需要檢測已更改的內容,並在數據庫中執行適當的SQL命令以添加新記錄,刪除舊記錄並更新現有記錄。 EF對此沒有任何影響,除非您不需要手動編寫這些SQL命令,但是您必須告訴EF它需要爲每條記錄執行哪項操作。 –

+0

我越來越討厭的是,你看到關於MVC和EF4的每一個演示/教程 - 顯示了更新簡單類型的一個非常基本的例子 - 沒有任何關於你會發現自己的常見情況。使用1-M或M-M關係。我對所有這些都很生氣! – Vidar