7

Me he sentido frustrado por esto como un día ... por favor ayuda.ASP.NET MVC 3 EF CodeFirst - DBContext artículo de edición

Tengo una sección de factura en la que puedo agregar nuevos elementos dinámicamente agregando/eliminando elementos DOM usando JQuery ... ya he descubierto cómo nombrar estos elementos correctamente para que se asignen en consecuencia y correctamente a el modelo y enviar al parámetro de acción.

Así que vamos a decir que tengo una acción en mi controlador llamado Editar con el parámetro de tipo de factura

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

El parámetro factura contiene todos los datos que quiero. No tengo un problema con esto El siguiente es el modelo de objeto.

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; } 
} 

Nota del InvoiceItems colección - contiene una colección de las posiciones de factura insertados de forma dinámica. El siguiente es el modelo 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; } 
} 

Y ahí es donde estoy atascado.

No puedo insertar/actualizar los elementos en Factura de la colección InvoiceItems en la base de datos por casualidad. He estado buscando en Google y he encontrado estas soluciones, lamentablemente ninguno de estos funciona.

Solución # 1 - un código extraño con 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(); 

Solución # 2 - la adición de un nuevo método de actualización a mi DbContext:

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

Solución # 3 - asociar una entidad existente pero modificada al contexto de acuerdo con 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(); 

Cómo la solución # 1 obras: actualiza todas las propiedades excepto InvoiceItems recién añadidos. SetValues ​​() ignora la propiedad ICollection. Esto funcionaría si añadiera esta línea justo antes de db.SaveChanges();

origEntry.Entity.InvoiceItems = invoice.InvoiceItems; 

... pero no creo que este sea el enfoque correcto en absoluto.

Cómo funciona la solución n. ° 2: no funciona en absoluto porque no hay ninguna propiedad ObjectContext en la clase DbContext.

Cómo funciona la solución n. ° 3: esta solución actualizará la factura pero, de lo contrario, ignorará InvoiceItems.


información adicional: siguiente es la pieza de javascript que se utiliza para generar los elementos artículo que se asignan a las propiedades InvoiceItem.

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" />', 
    }); 

La última pregunta: ¿qué estoy haciendo mal? ¿Qué hay de malo en tener una nueva colección de artículos en relación con otro modelo que se inserta automáticamente en la base de datos? ¿Por qué se ignora cuando el modelo principal se actualiza con éxito?

Editar 1: correcciones

+0

Cuando dices que "no puedes agregar los elementos", ¿qué significa eso exactamente? ¿Significa que se genera una excepción? Si es así, ¿cuál es la excepción? –

+0

Sí, ¿puede por favor publicar la excepción? – Jack

Respuesta

6

DbEntityEntry.CurrentValues.SetValues sólo actualiza escalar y propiedades complejas. No le importan las propiedades de navegación. Esa es la razón por la cual los cambios en su colección InvoiceItems no se escriben en la base de datos. El mismo problema es establecer el estado de Invoice en Modified. Esto solo marca propiedades escalares y complejas como propiedades de navegación modificadas pero no ninguna.

Desafortunadamente EF no ofrece un método simple que analice para usted qué elementos de una colección de navegación se han agregado, eliminado o modificado en comparación con el estado original en la base de datos si los cambios se realizaron mientras las entidades separado de un contexto (en una página web en su ejemplo).

Esto significa que tiene que hacer esta comparación entre el estado original en la base de datos y el nuevo estado cambiado usted mismo.

Ejemplos de código de cómo hacer que se encuentran en las respuestas aquí, por ejemplo: The relationship could not be changed because one or more of the foreign-key properties is non-nullable Los ejemplos se ajustan bastante bien a su situación, basta con sustituir ParentItem por Invoice y ChildItems por InvoiceItems.

También puede consultar esta pregunta de hace unos días para obtener infos adicionales: WCF, Entity Framework 4.1 and Entity's State En realidad, hay muchas otras preguntas que tienen que ver con el mismo problema que la falta de soporte para actualizar gráficos de objetos separados (que es escenario bastante común) es una de las limitaciones más desagradables de Entity Framework.

(Una nota: en un controlador MVC existe el método UpdateModel. No estoy seguro de si este método es capaz de actualizar un gráfico completo de objetos (agregar, eliminar y cambiar elementos en una colección). Personalmente no lo hago t usarlo, y si lo usaría no para trabajar con el modelo DB, solo para actualizaciones de ViewModel.)

+0

Gracias, esa es la información que necesitaba :) – Mirek

Cuestiones relacionadas