2012-09-13 9 views
5

Estoy haciendo una aplicación web MVC4 usando Entity Framework 5 (Base de datos con POCOs generados) para acceso a datos.¿Cómo vuelvo a conectar con gracia una estructura de POCO de Entity Framework 5 y la guardo?

En la aplicación, el usuario pasa por varias pantallas, creando o editando un documento (llamado 'caso de estudio'). Cuando llegan a la pantalla final, su documento existe como un POCO CaseStudy en la memoria, y todo es excelente hasta que es hora de guardar esta estructura en la base de datos.

Para almacenar el documento, he definido varias tablas de base de datos, que a su vez se asignan a POCO EF usadas por la capa empresarial, que luego son consumidas por los controladores MVC. Como tal, los DbContexts efímeros se utilizan para recuperar POCO y almacenarlos en sesión entre las solicitudes.

Como resultado, la pantalla de guardar debe guardar el contenido de este POCO que tiene propiedades de navegación para datos de tabla existentes (tablas de Categoría, Diseño y Secciones) y también datos agregados o actualizados (CaseStudySections y el CaseStudy mismo). Por lo tanto, todas las POCO son nuevas o el contexto utilizado para recuperarlas ya hace tiempo que se eliminó. En otras palabras, todos están "desapegados".

Lo inusual de esta publicación es que ya tengo una solución de trabajo a la mano. El problema es que es voluminoso, frágil y poco elegante. Estoy publicando el código a continuación. Tenga en cuenta la iteración a través de subcolecciones, las adiciones y agregados explícitos, tener que obtener un objeto de entrada y marcar las propiedades individuales modificadas para que se actualicen, y la horrible canción y baile al final para sincronizar la colección AdditionalMaterials. Si esto es lo que se requiere para lidiar con las POCO separadas en EF5, me sentiré decepcionado.

¿Falta algo aquí? ¿Esto es consistente con las mejores prácticas? ¿Hay una manera más elegante y/o concisa de adjuntar una estructura de POCO e insertar/actualizar?

El código para guardar un estudio de caso:

public void SaveCaseStudy(CaseStudy caseStudy) 
{ 
    foreach (var s in caseStudy.CaseStudySections) 
    { 
     this.Entities.Sections.Attach(s.Section); 

     if (s.CreatedByRefId == default(Guid)) 
     { 
      s.CreatedByRefId = this.UserRefId; 
      s.CreatedTime = DateTime.Now; 
      this.Entities.CaseStudySections.Add(s); 
     } 
     else 
     { 
      this.Entities.CaseStudySections.Attach(s); 
      var entry = this.Entities.Entry(s); 
      entry.Property(e => e.TextData).IsModified = true; 
      entry.Property(e => e.BinaryData).IsModified = true; 
     } 

     s.LastModifiedByRefId = this.UserRefId; 
     s.LastModifiedTime = DateTime.Now; 
    } 

    foreach (var m in caseStudy.AdditionalMaterials) 
    { 
     if (m.CreatedByRefId == default(Guid)) 
     { 
      m.CreatedByRefId = this.UserRefId; 
      m.CreatedTime = DateTime.Now; 
      this.Entities.AdditionalMaterials.Add(m); 
     } 
     else 
     { 
      this.Entities.AdditionalMaterials.Attach(m); 
     } 

     m.LastModifiedByRefId = this.UserRefId; 
     m.LastModifiedByTime = DateTime.Now; 
    } 

    this.Entities.Layouts.Attach(caseStudy.Layout); 
    this.Entities.Categories.Attach(caseStudy.Category); 

    if (caseStudy.CreatedByRefId != default(Guid)) 
    { 
     this.Entities.CaseStudies.Attach(caseStudy); 
     var entry = this.Entities.Entry(caseStudy); 
     entry.Property(e => e.CaseStudyName).IsModified = true; 
     entry.Property(e => e.CaseStudyTitle).IsModified = true; 
    } 
    else 
    { 
     this.Entities.CaseStudies.Add(caseStudy); 
     caseStudy.CreatedByRefId = this.UserRefId; 
     caseStudy.CreatedTime = DateTime.Now; 
    } 

    caseStudy.LastModifiedByRefId = this.UserRefId; 
    caseStudy.LastModifiedTime = DateTime.Now; 

    if (caseStudy.CaseStudyStatus != (int)CaseStudyStatus.Personalized) 
    { 
     caseStudy.CaseStudyStatus = (int)CaseStudyStatus.PendingApproval; 
    } 

    caseStudy.ApprovedByRefId = null; 
    caseStudy.ApprovedTime = null; 
    this.Entities.SaveChanges(); 

    var existingAdditionalMaterialRefIds = caseStudy.AdditionalMaterials 
     .Select(m => m.AdditionalMaterialRefId) 
     .ToArray(); 

    var additionalMaterialsToRemove = this.Entities.AdditionalMaterials 
     .Where(m => 
      m.CaseStudyRefId == caseStudy.CaseStudyRefId && 
      !existingAdditionalMaterialRefIds.Contains(m.AdditionalMaterialRefId)) 
     .ToArray(); 

    foreach (var additionalMaterialToRemove in additionalMaterialsToRemove) 
    { 
     this.Entities.AdditionalMaterials.Remove(additionalMaterialToRemove); 
    } 

    this.Entities.SaveChanges(); 
} 

Respuesta

3

En general, es lo que tiene que hacer. Debe informarle a EF sobre cada cambio que desea realizar al adjuntar un gráfico de objetos separados. No digo que su código no se pueda simplificar, pero igual tendrá que tratar con cada entidad y establecer su estado si desea que se agregue o modifique.

Here es un poco más antiguo, pero sigue siendo una respuesta válida sobre el tema - en resumen, nada ha cambiado desde que lo escribí, solo se creó una nueva API DbContext que todavía se encuentra sobre la antigua API. La mejor descripción de este tema que he visto hasta ahora está en el libro Programming Entity Framework: DbContext.

2

¿Qué tal hacer:

db.CaseStudies.Attach(caseStudy); 
db.Entry(caseStudy).State = EntityState.Modified; 
db.SaveChange(); 

que salvará a todos los cambios en su modelo para el PP.

+0

Esto no funciona, lamentablemente. En nuestro caso produce una DbUpdateConcurrencyException: 'Store update, insert o delete statement afectó a un número inesperado de filas (0). Las entidades pueden haber sido modificadas o eliminadas desde que se cargaron las entidades. Actualice las entradas de ObjectStateManager. Esto es probable porque el CaseStudy consiste en objetos de datos donde algunos ya existen en la base de datos (y, por lo tanto, aparentemente deben volverse a unir explícitamente), y algunos son nuevos y deben insertarse. –

Cuestiones relacionadas