2015-10-18 52 views
-2

我使用MVC 5與實體框架代碼優先。MVC中的UpdateModel創建子列表的重複/孤立記錄

我有一個班級(學校班級)有一個學生名單附加到它的對象,我試圖更新班級和/或學生在課堂上,當我在上下文中調用SaveChanges()時,我得到的是班級中學生的重複記錄。基本上,舊的學生列表在數據庫中「孤兒」,並且全新的一組學生被附加到數據庫中的編輯班級。

因此,而不是10在數據庫中糾正學生的記錄,我現在有20個學生記錄。 10個原始(未糾正)和10個新(糾正)的。原有的10個外鍵被刪除,因此它們不再是該類的一部分。

Class對象的任何更新,而無需複製類記錄實施罰款。

我見過一個回答表明,也許上下文不知道學生是不是新的項目,因此它增加了他們......並抓住從數據庫中的學生,讓上下文知道他們 - 但如果學生從數據庫中提取類對象時出現,是不是像直接拉動類似的東西一樣?

public class Class 
    { 
    [Key] 
    public Guid ClassId { get; set; } 
    [Display(Name="Class Name")] 
    public string ClassName { get; set; } 

    public virtual List<Student> Students { get; set; } 
    public virtual Teacher Teacher { get; set; } 

    public Class() 
    { 
     ClassId = Guid.NewGuid(); 
     Students = new List<Student>(); 
    } 

    } 

    [HttpPost] 
    public ActionResult Edit(Guid id, FormCollection collection) 
    {     
     Class selectedclass = db.Classes.Find(id); 

     try 
     {             
      UpdateModel(selectedclass, collection.ToValueProvider()); 
      db.SaveChanges(); 
      return RedirectToAction("Details", new { id = id }); 
     } 
     catch 
     { 
      return View(selectedclass); 
     } 
    } 

我在做什麼錯了?

,我能想到的唯一要做的是去除附加到該類之前我保存更改數據庫中所有學生的記錄,但必須有比這更好的方式。

db.Students.RemoveRange(db.Classes.Find(id).Students); 

所以,我試圖連接的學生名單範圍內,甚至改變他們的狀態來修改,如:

selectedclass.Students.ForEach(s => db.Students.Attach(s)); 
selectedclass.Students.ForEach(s => db.Entry(s).State = EntityState.Modified); 

,但沒有運氣,仍然得到重複和孤兒。

試圖直接從數據庫中獲取的學生,但沒有運氣:

var ids = selectedclass.Students.Select(s => s.StudentId).ToList();   

selectedclass.Students = db.Students.Where(s => ids.Contains(s.StudentId)).ToList() 

的HTML編輯器爲學生:

@for (int i = 0; i < Model.Students.Count; i++) 
    { 
     <div class="col-md-8"> 
      @Html.EditorFor(m => m.Students[i], new { htmlAttributes = new { @class = "form-control" }}) 
     </div> 
    } 

編輯模板學生:

<div class="form-group"> 
@Html.LabelFor(m => m.FirstName, htmlAttributes: new { @class = "control-label col-md-2" }) 
<div class="col-xs-6 col-md-4"> 
    @Html.EditorFor(m => m.FirstName, new { htmlAttributes = new { @class = "form-control" }}) 
</div> 
@Html.LabelFor(m => m.LastName, htmlAttributes: new { @class = "control-label col-md-2" }) 
<div class="col-xs-6 col-md-4"> 
    @Html.EditorFor(m => m.LastName, new { htmlAttributes = new { @class = "form-control" }}) 
</div> 
</div> 

<div class="form-group"> 
@Html.LabelFor(m => m.UserName, htmlAttributes: new { @class = "control-label col-md-2" }) 
<div class="col-xs-6 col-md-4"> 
    @Html.EditorFor(m => m.UserName, new { htmlAttributes = new { @class = "form-control" } }) 
</div> 
@Html.LabelFor(m => m.Password, htmlAttributes: new { @class = "control-label col-md-2" }) 
<div class="col-xs-6 col-md-4"> 
    @Html.EditorFor(m => m.Password, new { htmlAttributes = new { @class = "form-control" }}) 
</div> 
</div> 
+0

在使用'UpdateModel()'之前和之後檢查'selectedclass'的值# –

+0

@StephenMuecke它們從原來的狀態改變爲我希望它們更新的狀態...沒有問題。它只是將新列表添加到數據庫中,而不是更新現有的學生列表,而是將其添加到數據庫中。 –

+0

但你在你的問題中指出ClassId屬性被刪除? –

回答

1

我認爲發生的事情是您正在使用Class selectedclass = db.Classes.Find(id);,但那不是自動加載學生的集合。

我懷疑是正在發生的事情是,因爲沒有加載Students收集,最有可能的UpdateModel只是調用Students setter,它取代集合。

由於Student對象沒有被加載背景下,實體框架認爲他們是新同學,並相應地將它們插入。見Data Points - Why Does Entity Framework Reinsert Existing Objects into My Database?

有一對夫婦解決這個。在致電UpdateModel之前,您可以嘗試顯式加載學生館藏。或者,您可以通過明確地將它們附加到上下文來明確告訴實體框架,即學生記錄是存在的。但是,一般來說,我儘量避免處理斷開連接的實體 - 實體框架斷開連接的實體處理中有很多「陷阱」。

另外,直接綁定到實體框架映射類時要小心,因爲這很容易導致重複發佈/落後安全漏洞。請參閱ASP.NET MVC – Think Before You Bind

更新 我開始懷疑問題在於html如何指定表單域。請參閱Model Binding To A List

您的其他選項當然是而不是以這種方式使用UpdateModel。相反,只需加載實體並「手動」將編輯應用到加載的實體。從安全角度來看,這不太可能導致問題。在這種情況下,我可能會推薦移動到強類型視圖,以便您不必使用字典鍵搜索表單集合。

+0

當我遍歷代碼時,selectedClass上的學生將在加載selectedClass後出現在那裏。我已經嘗試明確加載學生 - 似乎沒有工作。附加他們,改變他們的狀態 - 似乎沒有工作。 –

+0

您是否啓用延遲加載?如果是這樣,只需在調試器中觀察變量就可能觸發延遲加載,這樣在調試時看到的內容並不一定是在調試之外運行時發生的情況 - 具體取決於UpdateModel的內部工作方式。雖然你也明確地加載了學生集合,但我開始認爲問題在於你的html沒有根據默認模型聯編程序約定呈現id字段。 – Nathan

+1

@BarryFranklin - 我的編輯模板中沒有看到你實際呈現'StudentId'的任何地方。它至少需要作爲隱藏字段存在,例如'@ H​​tml.HiddenFor(m => m.StudentId)' – Nathan