ASP.NET MVC3 Code-First Error: intentar actualizar la entidad

He comprobado esta pregunta y parece estar relacionado con lo que necesito, pero no responde exactamente.

Tengo una entidad (Sql Compact usando EF Code First a través de MVC3, si eso no estaba claro en el título) para un "Problema" (seguimiento genérico de problemas, solo para mi propia comprensión educativa de cómo funciona MVC3). La clase Issue tiene una propiedad CreatedBy (referencia Int a un usuario que creó el problema) y una propiedad CreatedDate (DateTime). Cuando utilizo el código andamio para actualizar (modificado solo para evitar que el usuario modifique algunos campos de fecha actualizados):

        if (ModelState.IsValid)
        {
            issue.LastActivity = (DateTime?)DateTime.Now.Date;
            if (issue.ClosedBy != null) issue.ClosedDate = (DateTime?)DateTime.Now.Date;
            startingIssue = null;
            db.Entry(issue).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }

Recibo el error mencionado en la pregunta vinculada (conversión de un tipo de datos datetime2 a un tipo de datos datetime, etc., etc.)

Cuando paso por el código, parece que mis propiedades CreatedBy y CreatedDate no están contenidas en la instancia de issue que el controlador está pasando. Cuando trato de solucionarlo tomando otra copia del problema de la base de datos y actualizándola a valores:

        var startingIssue = db.Issues.Find(issue.IssueId);
        if (ModelState.IsValid)
        {
            if (issue.CreatedBy != startingIssue.CreatedBy) issue.CreatedBy = startingIssue.CreatedBy;
            if (issue.CreatedDate != startingIssue.CreatedDate) issue.CreatedDate = startingIssue.CreatedDate;
            issue.LastActivity = (DateTime?)DateTime.Now.Date;
            if (issue.ClosedBy != null) issue.ClosedDate = (DateTime?)DateTime.Now.Date;
            startingIssue = null;
            db.Entry(issue).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }

Recibo la infracción de concurrencia: ya existe un objeto con la misma clave en ObjectStateManager. ObjectStateManager no puede rastrear varios objetos con la misma clave.

Entonces, ¿cómo puedo hacer que EF vea la fecha que ya está configurada en la base de datos (para que no intente actualizar la fecha de creación a 1/1/0001) sin violar la concurrencia?

Editar Está bien ... lo encontré. Yo estaba, aparentemente, buscando @Html.HiddenFor(model => model.[property]) y agregando el editor a la vista de todos modos. Eso me parece un poco tonto y rotundo, pero funciona sin tener que agregar un código personalizado para separar un objeto y sustituirlo por uno actualizado.

preguntado el 16 de mayo de 11 a las 19:05

2 Respuestas

La respuesta corta es que ya ha cargado la entidad en el contexto con el Find y luego no podrá adjuntar otro.

Te quedan dos opciones:

  • Separe la primera instancia, luego adjunte la segunda
  • Copie los campos de la segunda instancia a la primera

Compartiré el código de la primera opción. Primero, agregue un Detach método a tu DbContext implementación:

public void Detach(object entity)
{
    var objectContext = ((IObjectContextAdapter)this).ObjectContext;
    objectContext.Detach(entity);
}

Luego llame Detach en lugar de establecer la variable en null

var startingIssue = db.Issues.Find(issue.IssueId);
if (ModelState.IsValid)
{
    if (issue.CreatedBy != startingIssue.CreatedBy) issue.CreatedBy = startingIssue.CreatedBy;
    if (issue.CreatedDate != startingIssue.CreatedDate) issue.CreatedDate = startingIssue.CreatedDate;
    issue.LastActivity = (DateTime?)DateTime.Now.Date;
    if (issue.ClosedBy != null) issue.ClosedDate = (DateTime?)DateTime.Now.Date;

    // startingIssue = null;
    db.Detach(startingIssue);

    db.Entry(issue).State = EntityState.Modified;
    db.SaveChanges();
    return RedirectToAction("Index");
}

contestado el 16 de mayo de 11 a las 23:05

Eso funciona. Gracias. Esperaré a ver si alguien más proporciona una solución más elegante, pero si no, elegiré la tuya como la respuesta aceptada. - AllenG

Tuve un problema similar. Tengo campos de auditoría en mis tablas (CreatedBy, CreatedOn) que necesito configurar cuando creo un nuevo registro, pero no quiero pasarlos para editar páginas en mi sitio MVC. Entonces, cuando se está editando un nuevo registro, tengo que leer los valores de auditoría actuales de la base de datos y luego actualizar el objeto entrante antes de guardarlo en el DBContext. Para hacer esto, necesito .Find (id), tomar el valor actual y luego Separar. Mi pregunta / respuesta está aquí stackoverflow.com/questions/8145635/… - kingdango

@Ed: Me gustaría dar un + 100k ... ¡He perdido horas en ese problema! ¡¡Gracias!! - David

Si los campos CreateDate y CreatedBy no están en el formulario de edición, el objeto de acción de actualización no tendrá los valores db.

La llamada adicional a la base de datos y el restablecimiento, como describe la respuesta de Ed, se pueden evitar si incluye esos campos en el formulario de edición. Luego, el enlace normal del modelo debería recogerlos y devolvérselos en la actualización.

contestado el 16 de mayo de 11 a las 23:05

De hecho, podrían, pero también serían editables. Ya muestro los valores, como texto. Si agrego el campo editor para los valores, tendré que esforzarme en otras formas para evitar que un usuario decida que su problema se creó en realidad hace 10 días en lugar de solo hoy. - AllenG

Sí, convertirlos en campos ocultos haría que Binding funcionara, pero expondría esos campos a manipulación, lo que no es bueno si los campos deben permanecer constantes. - kingdango

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.