Entity Framework 4, objetos POCO y ASP.Net MVC2. Tengo una relación de muchos a muchos, digamos entre las entidades BlogPost y Tag. Esto significa que en mi clase BlogPost de POCO generada por T4 tengo:
public virtual ICollection<Tag> Tags {
// getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;
Solicito un BlogPost y las etiquetas relacionadas de una instancia de ObjectContext y lo envío a otra capa (Ver en la aplicación MVC). Más tarde recupero el BlogPost actualizado con propiedades cambiadas y relaciones cambiadas. Por ejemplo, tenía etiquetas "A", "B" y "C", y las nuevas etiquetas son "C" y "D". En mi ejemplo particular, no hay etiquetas nuevas y las propiedades de las etiquetas nunca cambian, por lo que lo único que se debe guardar son las relaciones cambiadas. Ahora necesito guardar esto en otro ObjectContext. (Actualización: ahora intenté hacerlo en la misma instancia de contexto y también fallé).
El problema: no puedo hacer que salve las relaciones correctamente. Probé todo lo que encontré:
- Controller.UpdateModel y Controller.TryUpdateModel no funcionan.
- Obtener el BlogPost antiguo del contexto y luego modificar la colección no funciona. (con diferentes métodos del siguiente punto)
- Esto probablemente funcionaría, pero espero que esto sea solo una solución alternativa, no la solución :(.
- Probé las funciones Adjuntar / Agregar / CambiarObjetoState para BlogPost y / o Etiquetas en todas las combinaciones posibles. Ha fallado.
- Esto parece lo que necesito, pero no funciona (traté de arreglarlo, pero no puedo por mi problema).
- Intenté ChangeState / Add / Attach / ... los objetos de relación del contexto. Ha fallado.
"No funciona" significa en la mayoría de los casos que trabajé en la "solución" dada hasta que no produce errores y guarda al menos las propiedades de BlogPost. Lo que sucede con las relaciones varía: por lo general, las etiquetas se agregan nuevamente a la tabla de etiquetas con nuevas PK y el BlogPost guardado hace referencia a esas y no a las originales. Por supuesto, las etiquetas devueltas tienen PK, y antes de los métodos de guardar / actualizar, verifico las PK y son iguales a las de la base de datos, por lo que probablemente EF piense que son objetos nuevos y esas PK son las temporales.
Un problema que conozco y podría hacer imposible encontrar una solución simple automatizada: cuando se cambia la colección de un objeto POCO, eso debería suceder mediante la propiedad de colección virtual mencionada anteriormente, porque entonces el truco FixupCollection actualizará las referencias inversas en el otro extremo de la relación de muchos a muchos. Sin embargo, cuando una vista "devuelve" un objeto BlogPost actualizado, eso no sucedió. Esto significa que tal vez no haya una solución simple para mi problema, pero eso me entristecería mucho y odiaría el triunfo de EF4-POCO-MVC :(. También eso significaría que EF no puede hacer esto en el entorno MVC cualquiera que sea Se utilizan tipos de objetos EF4 :(. Creo que el seguimiento de cambios basado en instantáneas debería descubrir que el BlogPost modificado tiene relaciones con las etiquetas con los PK existentes.
Por cierto: creo que ocurre el mismo problema con las relaciones uno a muchos (Google y mi colega lo dicen). Lo intentaré en casa, pero incluso si funciona, eso no me ayuda en mis seis relaciones de muchos a muchos en mi aplicación :(.
fuente
Respuestas:
Intentémoslo de esta manera:
Editar:
Supongo que uno de mis comentarios te dio falsas esperanzas de que EF haría la fusión por ti. Jugué mucho con este problema y mi conclusión dice que EF no hará esto por ti. Creo que también ha encontrado mi pregunta en MSDN . En realidad, hay muchas preguntas de este tipo en Internet. El problema es que no se indica claramente cómo afrontar este escenario. Así que echemos un vistazo al problema:
Antecedentes del problema
EF necesita rastrear los cambios en las entidades para que la persistencia sepa qué registros deben actualizarse, insertarse o eliminarse. El problema es que es responsabilidad de ObjectContext realizar un seguimiento de los cambios. ObjectContext puede rastrear cambios solo para entidades adjuntas. Las entidades que se crean fuera de ObjectContext no se rastrean en absoluto.
Descripción del problema
Según la descripción anterior, podemos afirmar claramente que EF es más adecuado para escenarios conectados donde la entidad siempre está adjunta al contexto, típico de la aplicación WinForm. Las aplicaciones web requieren un escenario desconectado donde el contexto se cierra después del procesamiento de la solicitud y el contenido de la entidad se pasa como respuesta HTTP al cliente. La siguiente solicitud HTTP proporciona contenido modificado de la entidad que debe recrearse, adjuntarse al nuevo contexto y conservarse. La recreación generalmente ocurre fuera del alcance del contexto (arquitectura en capas con ignorancia persistente).
Solución
Entonces, ¿cómo lidiar con un escenario tan desconectado? Cuando usamos clases de POCO, tenemos 3 formas de lidiar con el seguimiento de cambios:
La sincronización manual en una sola entidad es una tarea sencilla. Solo necesita adjuntar la entidad y llamar a AddObject para insertar, DeleteObject para eliminar o establecer el estado en ObjectStateManager en Modified para actualizar. El verdadero dolor surge cuando tienes que lidiar con un gráfico de objetos en lugar de una entidad única. Este dolor es aún peor cuando tiene que lidiar con asociaciones independientes (aquellas que no usan propiedad de clave externa) y muchas a muchas relaciones. En ese caso, debe sincronizar manualmente cada entidad en el gráfico de objetos, pero también cada relación en el gráfico de objetos.
La documentación de MSDN propone la sincronización manual como solución: Adjuntar y desconectar objetos dice:
Los métodos mencionados son ChangeObjectState y ChangeRelationshipState de ObjectStateManager = seguimiento manual de cambios. Una propuesta similar se encuentra en otro artículo de documentación de MSDN: Defining and Managing Relationships dice:
Además, hay una publicación de blog relacionada con EF v1 que critica exactamente este comportamiento de EF.
Razón de la solución
EF tiene muchas operaciones "útiles" y la configuración como de actualización , de carga , applyCurrentValues , ApplyOriginalValues , MergeOption etc, pero por mi investigación todas estas operaciones de trabajo sólo para una sola entidad y afecta sólo a preperties escalares (= no en las propiedades de navegación y relaciones). Prefiero no probar estos métodos con tipos complejos anidados en la entidad.
Otra solución propuesta
En lugar de la funcionalidad Merge real, el equipo de EF proporciona algo llamado Entidades de seguimiento automático (STE) que no resuelven el problema. En primer lugar, STE funciona solo si se utiliza la misma instancia para todo el procesamiento. En la aplicación web no es el caso a menos que almacene la instancia en el estado de vista o sesión. Debido a eso, no estoy contento con el uso de EF y voy a verificar las características de NHibernate. La primera observación dice que NHibernate quizás tenga tal funcionalidad .
Conclusión
Terminaré con estas suposiciones con un solo enlace a otra pregunta relacionada en el foro de MSDN. Compruebe la respuesta de Zeeshan Hirani. Es autor de Entity Framework 4.0 Recipes . Si dice que no se admite la fusión automática de gráficos de objetos, le creo.
Pero aún existe la posibilidad de que esté completamente equivocado y exista alguna funcionalidad de fusión automática en EF.
Edición 2:
Como puede ver, esto ya se agregó a MS Connect como sugerencia en 2007. MS lo ha cerrado como algo que se debe hacer en la próxima versión, pero en realidad no se ha hecho nada para mejorar esta brecha, excepto STE.
fuente
Tengo una solución al problema que fue descrito anteriormente por Ladislav. He creado un método de extensión para DbContext que realizará automáticamente la función de agregar / actualizar / eliminar en función de una diferencia del gráfico proporcionado y el gráfico persistente.
Eche un vistazo y vea si puede ayudar http://refactorthis.wordpress.com/2012/12/11/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a- gráfico-de-entidades-separadas /
Puede ir directamente al código aquí https://github.com/refactorthis/GraphDiff
fuente
Sé que es tarde para el OP, pero dado que este es un problema muy común, publiqué esto en caso de que le sirva a otra persona. He estado jugando con este problema y creo que tengo una solución bastante simple, lo que hago es:
En el siguiente ejemplo, "dataobj" y "_categories" son los parámetros recibidos por mi controlador "dataobj" es mi objeto principal, y "_categories" es un IEnumerable que contiene los ID de las categorías que el usuario seleccionó en la vista.
Incluso funciona para múltiples relaciones.
fuente
El equipo de Entity Framework es consciente de que se trata de un problema de usabilidad y planea solucionarlo después de EF6.
Del equipo de Entity Framework:
Si esto le afecta, vote por la función en
http://entityframework.codeplex.com/workitem/864
fuente
Todas las respuestas fueron excelentes para explicar el problema, pero ninguna de ellas realmente me resolvió el problema.
Descubrí que si no usaba la relación en la entidad principal, sino que simplemente agregaba y eliminaba las entidades secundarias, todo funcionaba bien.
Lo siento por el VB, pero eso es en lo que está escrito el proyecto en el que estoy trabajando.
La entidad padre "Informe" tiene una relación de uno a muchos con "ReportRole" y tiene la propiedad "ReportRoles". Los nuevos roles se transmiten mediante una cadena separada por comas de una llamada Ajax.
La primera línea eliminará todas las entidades secundarias, y si usara "report.ReportRoles.Remove (f)" en lugar de "db.ReportRoles.Remove (f)", obtendría el error.
fuente