Asp.net

ASP.NET MVC 3 EF CodeFirst - DBContext 項目編輯

  • August 13, 2011

我已經為此感到沮喪了一天……請幫忙。

我有一個發票部分,我可以在其中通過使用 JQuery 附加/刪除 DOM 元素來動態添加新項目……我已經想出瞭如何正確命名這些元素,以便它們正確地映射到模型並發送到動作參數。

因此,假設我的控制器中有一個名為 Edit 的操作,其參數類型為 Invoice

[HttpPost]
public virtual ActionResult Edit(Invoice invoice) {
   if (ModelState.IsValid) {
        //code discussed below
   }
   return RedirectToAction("Index");
}

invoice 參數包含我想要的所有數據。我對此沒有問題。下面是對像模型。

public class Invoice
{
   [Key]
   public int ID { get; set; }
   public InvoiceType InvoiceType { get; set; }
   public string Description { get; set; }
   public int Amount { get; set; }
   public virtual ICollection<InvoiceItem> InvoiceItems { get; set; }
}

請注意InvoiceItems集合 - 它包含動態插入的發票項目的集合。下面是 InvoiceItem 模型

[Table("InvoiceItems")]
public class InvoiceItem
{
   [Key]
   public int ID { get; set; }

   [ForeignKey("Invoice")]
   public int InvoiceID { get; set; }
   public Invoice Invoice { get; set; }

   public string Description { get; set; }
   public int Amount { get; set; }
}

這就是我卡住的地方。

我無法將 Invoice 集合 InvoiceItems 中的項目插入/更新到數據庫中。我一直在Google搜尋,我找到了這些解決方案 - 不幸的是,這些都不起作用。

解決方案 #1 - 一些帶有 SetValues 的奇怪程式碼:

Invoice origInvoice = db.Invoices.Single(x => x.ID == invoice.ID);
var entry = db.Entry(origInvoice);
entry.OriginalValues.SetValues(invoice);
entry.CurrentValues.SetValues(invoice);
db.SaveChanges();

解決方案 #2 - 向我的 DbContext 添加新的更新方法:

public void Update<T>(T entity) where T : class
{
  this.Set<T>().Attach(entity);
  base.ObjectContext.ObjectStateManager.ChangeObjectState(entity,System.Data.EntityState.Modified);
}

解決方案 #3 - 根據http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part將現有但已修改的實體附加到上下文-4-add-attach-and-entity-states.aspx

db.Entry(invoice).State = EntityState.Modified;
db.SaveChanges();

**解決方案 #1 的工作原理:**更新除新添加的 InvoiceItems 之外的所有屬性。SetValues() 忽略 ICollection 屬性。如果我在 db.SaveChanges(); 之前添加這一行,這將起作用。

origEntry.Entity.InvoiceItems = invoice.InvoiceItems;

…但我根本不相信這是正確的方法。

**解決方案 #2 的工作原理:**根本不起作用,因為 DbContext 類中沒有 ObjectContext 屬性。

**解決方案 #3 的工作原理:**此解決方案將更新發票,但忽略 InvoiceItems。


附加資訊:以下是用於生成映射到 InvoiceItem 屬性的項目元素的一段 javascript。

   window.InvoiceItemIndex++;

   var str = $.tmpl("itemRowTmpl", {
       itemIndexes: '<input type="hidden" name="Invoice.InvoiceItems.Index" value="' + window.InvoiceItemIndex + '"/> \
                     <input type="hidden" name="Invoice.InvoiceItems[' + window.InvoiceItemIndex + '].ID" value="0"/>\
                     <input type="hidden" name="Invoice.InvoiceItems[' + window.InvoiceItemIndex + '].InvoiceID" value="@Model.Invoice.ID"/>',
       itemDescription: '<textarea cols="150" name="Invoice.InvoiceItems[' + window.InvoiceItemIndex + '].Description" rows="2"></textarea>',
       itemAmount: '<input type="text" class="InvoiceItemAmount" style="text-align:right;" name="Invoice.InvoiceItems[' + window.InvoiceItemIndex + '].Amount" />',
   });

最後一個問題:我做錯了什麼?將與另一個模型相關的新項目集合自動插入數據庫有什麼問題?當父模型成功更新時,為什麼會被忽略?

編輯1:更正

DbEntityEntry.CurrentValues.SetValues僅更新標量和復雜屬性。它不關心導航屬性。這就是為什麼您的InvoiceItems集合中的更改不會寫入數據庫的原因。同樣的問題是設置Invoiceto的狀態Modified。這僅將標量和復雜屬性標記為已修改,而不是任何導航屬性。

不幸的是,EF 沒有提供一種簡單的方法來分析導航集合中的哪些項目已添加、刪除或僅與數據庫中的原始狀態相比進行了修改,如果在實體與上下文分離時已完成更改(在您的範例中的網頁上)。

這意味著您必須自己對數據庫中的原始狀態和新更改的狀態進行比較。

如何做到這一點的程式碼範例在此處的答案中,例如:無法更改關係,因為一個或多個外鍵屬性不可為空這些範例非常適合您的情況,只需替換ParentItemInvoiceChildItemsby InvoiceItems

您還可以查看幾天前的這個問題以獲取更多資訊:WCF、Entity Framework 4.1 和 Entity’s State實際上還有許多其他問題與缺乏對更新分離對像圖的支持相同的問題(這是一個非常常見的場景)是實體框架更令人不快的限制之一。

(注意:在 MVC 控制器中有該UpdateModel方法。我不確定此方法是否能夠更新完整的對像圖(添加、刪除和更改集合中的項目)。我個人不使用它,並且如果我會使用它,那麼不要使用 DB 模型,僅用於 ViewModel 更新。)

引用自:https://stackoverflow.com/questions/7050235