7

我一直为这样的一天感到沮丧...请帮助。ASP.NET MVC 3 EF CodeFirst - DBContext项目编辑

我有一个发票部分,我可以通过使用JQuery添加/删除DOM元素来动态地添加新项目...我已经想出了如何正确命名这些元素,因此它们因此被正确地映射到模型并发送到动作参数。

所以我们可以说我在我的控制器称为编辑类型发票

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

发票参数包含了所有我想要的数据的PARAM动作。我没有这个问题。以下是对象模型。

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中的项目插入/更新到数据库中。我一直在使用谷歌搜索,我发现这些解决方案 - 这些工作都不幸。

解决方案#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:校正

+0

当你说你“无法添加项目”时,这是什么意思?这是否意味着会产生异常?如果是这样,什么是例外? –

+0

是的,你可以请发布例外? – Jack

回答

6

DbEntityEntry.CurrentValues.SetValues仅更新标量和复杂性。它不关心导航属性。这就是为什么您的InvoiceItems集合中的更改没有写入数据库的原因。同样的问题是将Invoice的状态设置为Modified。这只会将标量和复杂属性标记为已修改但不包含任何导航属性。

不幸的是,EF并没有提供一个简单的方法来分析导航集合中的哪些项目与数据库中的原始状态相比已经添加,删除或只修改过,如果这些更改已经完成从上下文中分离出来(在您的示例中的网页上)。

这意味着您必须在数据库中的原始状态与新更改的状态之间进行比较。

示例代码怎么做,都在这里的答案,例如:The relationship could not be changed because one or more of the foreign-key properties is non-nullable例子适应得很好,以你的情况,仅仅通过更换InvoiceItems通过ParentItemInvoiceChildItems

您还可以从几天前看看这个问题以获取更多信息:WCF, Entity Framework 4.1 and Entity's State实际上还有很多其他问题与同一问题有关,因为缺少对更新分离对象图的支持(这是一个相当常见的情况)是实体框架更令人不快的限制之一。 (注意:在MVC控制器中有UpdateModel方法,我不确定这个方法是否能够更新一个完整的对象图(添加,删除和更改集合中的项目)。我个人不喜欢'不使用它,如果我使用它,那么不适用于使用数据库模型,仅适用于ViewModel更新。)

+0

谢谢,这是我需要的信息:) – Mirek