I have been frustrated by this for like a day...please help.
I have an invoice section in which I'm able to add new items dynamically by appending/removing DOM elements using JQuery...i have already figured out how to name these elements correctly so they are consequently and properly mapped to the model and send to the action parameter.
So let's say I have an action in my controller called Edit with param of type Invoice
[HttpPost]
public virtual ActionResult Edit(Invoice invoice) {
if (ModelState.IsValid) {
//code discussed below
}
return RedirectToAction("Index");
}
The invoice parameter contains all the data I want. I don't have a problem with this. The following is the object model.
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; }
}
Note the InvoiceItems collection - this contains a collection of the dynamically inserted invoice items. The following is the InvoiceItem model
[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; }
}
And that's where I am stuck.
I can't insert/update the items in the Invoice collection InvoiceItems into the database by any chance. I've been googling a lot and I've found these solutions - none of these works unfortunately.
Solution #1 - some weird code with 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();
Solution #2 - adding a new update method to my DbContext:
public void Update<T>(T entity) where T : class
{
this.Set<T>().Attach(entity);
base.ObjectContext.ObjectStateManager.ChangeObjectState(entity,System.Data.EntityState.Modified);
}
Solution #3 - attaching an existing but modified entity to the context according to 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();
How solution #1 works: updates all the properties except newly added InvoiceItems. SetValues() ignores the ICollection property. This would work if I added this line just before db.SaveChanges();
origEntry.Entity.InvoiceItems = invoice.InvoiceItems;
...but I don't believe that this is the correct approach at all.
How solution #2 works: doesn't work at all because there is no ObjectContext property in the DbContext class.
How solution #3 works: this solution will update the invoice but otherwise ignores InvoiceItems.
Additional info: following is the piece of javascript that is used to generate the item elements which are mapped to the InvoiceItem properties.
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" />',
});
The final question: what am I doing wrong? What is wrong with having a new collection of items in relation to another model automatically inserted into the database? Why is it being ignored, when the parent model is updated sucessfully?
Edit 1: corrections