Recibo este error cuando obtengo GetById () en una entidad y luego establezco la colección de entidades secundarias en mi nueva lista que proviene de la vista MVC.
La operación falló: la relación no se pudo cambiar porque una o más de las propiedades de clave externa no son anulables. Cuando se realiza un cambio en una relación, la propiedad de clave externa relacionada se establece en un valor nulo. Si la clave externa no admite valores nulos, se debe definir una nueva relación, se debe asignar a la propiedad de clave externa otro valor no nulo o se debe eliminar el objeto no relacionado.
No entiendo esta línea:
La relación no se pudo cambiar porque una o más de las propiedades de clave externa no son anulables.
¿Por qué cambiaría la relación entre 2 entidades? Debe permanecer igual durante toda la vida útil de toda la aplicación.
El código en el que se produce la excepción es la simple asignación de clases secundarias modificadas en una colección a la clase primaria existente. Con suerte, esto atenderá la eliminación de las clases secundarias, la adición de nuevas y modificaciones. Pensé que Entity Framework maneja esto.
Las líneas de código se pueden destilar para:
var thisParent = _repo.GetById(1);
thisParent.ChildItems = modifiedParent.ChildItems();
_repo.Save();
Respuestas:
Debe eliminar los elementos secundarios antiguos
thisParent.ChildItems
uno por uno manualmente. Entity Framework no hace eso por ti. Finalmente, no puede decidir qué desea hacer con los elementos secundarios antiguos: si desea tirarlos o si desea conservarlos y asignarlos a otras entidades principales. Debe informar a Entity Framework su decisión. Pero tiene que tomar una de estas dos decisiones, ya que las entidades secundarias no pueden vivir solas sin una referencia a ningún padre en la base de datos (debido a la restricción de clave externa). Eso es básicamente lo que dice la excepción.Editar
Lo que haría si se pudieran agregar, actualizar y eliminar elementos secundarios:
Nota: Esto no está probado. Se supone que la colección de elementos secundarios es de tipo
ICollection
. (Normalmente lo tengoIList
y luego el código se ve un poco diferente). También eliminé todas las abstracciones del repositorio para simplificarlo.No sé si es una buena solución, pero creo que se debe hacer algún tipo de trabajo duro en este sentido para hacer frente a todo tipo de cambios en la colección de navegación. También estaría feliz de ver una manera más fácil de hacerlo.
fuente
La razón por la que enfrenta esto se debe a la diferencia entre composición y agregación .
En composición, el objeto hijo se crea cuando se crea el padre y se destruye cuando se destruye su padre . Entonces su vida útil es controlada por su padre. Por ejemplo, una publicación de blog y sus comentarios. Si se elimina una publicación, se deben eliminar sus comentarios. No tiene sentido tener comentarios para una publicación que no existe. Lo mismo para pedidos y artículos de pedido.
En la agregación, el objeto hijo puede existir independientemente de su padre . Si el padre se destruye, el objeto hijo aún puede existir, ya que se puede agregar a un padre diferente más adelante. por ejemplo: la relación entre una lista de reproducción y las canciones en esa lista de reproducción. Si se elimina la lista de reproducción, las canciones no deberían eliminarse. Se pueden agregar a una lista de reproducción diferente.
La forma en que Entity Framework diferencia las relaciones de agregación y composición es la siguiente:
Para la composición: espera que el objeto hijo tenga una clave primaria compuesta (ParentID, ChildID). Esto es por diseño ya que las identificaciones de los niños deben estar dentro del alcance de sus padres.
Para la agregación: espera que la propiedad de clave externa en el objeto secundario sea anulable.
Entonces, la razón por la que tiene este problema es por cómo ha configurado su clave principal en su tabla secundaria. Debe ser compuesto, pero no lo es. Por lo tanto, Entity Framework ve esta asociación como agregación, lo que significa que, cuando elimina o borra los objetos secundarios, no va a eliminar los registros secundarios. Simplemente eliminará la asociación y establecerá la columna de clave externa correspondiente en NULL (para que esos registros secundarios puedan asociarse más tarde con un padre diferente). Como su columna no permite NULL, obtiene la excepción que mencionó.
Soluciones:
1- Si tiene una buena razón para no querer usar una clave compuesta, debe eliminar los objetos secundarios explícitamente. Y esto puede hacerse más simple que las soluciones sugeridas anteriormente:
2- De lo contrario, al establecer la clave primaria adecuada en su tabla secundaria, su código tendrá más significado:
fuente
Este es un gran problema. Lo que realmente sucede en su código es esto:
Parent
desde la base de datos y obtiene una entidad adjuntaAhora la solución realmente depende de lo que quieras hacer y de cómo te gustaría hacerlo.
Si está utilizando ASP.NET MVC, puede intentar usar UpdateModel o TryUpdateModel .
Si solo desea actualizar los hijos existentes manualmente, simplemente puede hacer algo como:
En realidad, no es necesario adjuntar (establecer el estado
Modified
también adjuntará la entidad) pero me gusta porque hace que el proceso sea más obvio.Si desea modificar existente, eliminar existente e insertar nuevos hijos, debe hacer algo como:
fuente
.Clone()
. ¿Tiene en cuenta el caso de que aChildItem
tiene otras propiedades de navegación secundarias? Pero en ese caso, ¿no querríamos que todo el gráfico secundario se adjunte al contexto, ya que esperaríamos que todos los hijos secundarios sean objetos nuevos si el hijo en sí es nuevo? (Bueno, podría ser diferente de un modelo a otro, pero vamos a suponer el caso de que los sub-niño son "dependientes" del niño como los del niño dependen de los padres.)http://stackoverflow.com/questions/20233994/do-i-need-to-create-a-dbset-for-every-table-so-that-i-can-persist-child-entitie
Encontré esta respuesta mucho más útil para el mismo error. Parece que a EF no le gusta cuando eliminas, prefiere Eliminar.
Puede eliminar una colección de registros adjuntos a un registro como este.
En el ejemplo, todos los registros detallados adjuntos a una orden tienen su estado establecido en Eliminar. (En preparación para Agregar Detalles actualizados nuevamente, como parte de una actualización de Pedido)
fuente
¡No tengo idea de por qué las otras dos respuestas son tan populares!
Creo que tenía razón al suponer que el marco ORM debería manejarlo; después de todo, eso es lo que promete ofrecer. De lo contrario, su modelo de dominio se corrompe por problemas de persistencia. NHibernate maneja esto felizmente si configura la configuración de cascada correctamente. En Entity Framework también es posible, solo esperan que sigas mejores estándares al configurar tu modelo de base de datos, especialmente cuando tienen que inferir qué cascada se debe hacer:
Debe definir la relación padre-hijo correctamente utilizando una " relación de identificación ".
Si hace esto, Entity Framework sabe que el objeto secundario es identificado por el padre y, por lo tanto, debe ser una situación de "cascade-delete-huérfanos".
Aparte de lo anterior, es posible que necesite (de la experiencia NHibernate)
en lugar de reemplazar la lista por completo.
ACTUALIZAR
El comentario de @ Slauma me recordó que las entidades separadas son otra parte del problema general. Para resolver eso, puede utilizar un archivador de modelo personalizado que construya sus modelos intentando cargarlo desde el contexto. Esta publicación de blog muestra un ejemplo de lo que quiero decir.
fuente
parent.ChildItems.Remove
lugar de_dbContext.ChildItems.Remove
. Todavía hay (EF <= 6) sin soporte incorporado de EF para evitar un código largo como el de las otras respuestas.return context.Items.Find(id) ?? new Item()
Si está utilizando AutoMapper con Entity Framework en la misma clase, puede encontrar este problema. Por ejemplo si tu clase es
Esto intentará copiar ambas propiedades. En este caso, ClassBId no es anulable. Como AutoMapper copiará,
destination.ClassB = input.ClassB;
esto causará un problema.Configure su AutoMapper para Ignorar
ClassB
propiedad.fuente
Acabo de tener el mismo error. Tengo dos tablas con una relación padre-hijo, pero configuré un "en cascada de eliminación" en la columna de clave externa en la definición de tabla de la tabla hijo. Entonces, cuando elimino manualmente la fila principal (a través de SQL) en la base de datos, eliminará automáticamente las filas secundarias.
Sin embargo, esto no funcionó en EF, apareció el error descrito en este hilo. La razón de esto fue que en mi modelo de datos de entidad (archivo edmx) las propiedades de la asociación entre la tabla primaria y la tabla secundaria no eran correctas. La
End1 OnDelete
opción se configuró para sernone
("End1" en mi modelo es el final que tiene una multiplicidad de 1).Cambié manualmente la
End1 OnDelete
opciónCascade
y funcionó. No sé por qué EF no puede recoger esto cuando actualizo el modelo de la base de datos (tengo un primer modelo de base de datos).Para completar, así es como se ve mi código para eliminar:
Si no tuviera una eliminación en cascada definida, tendría que eliminar las filas secundarias manualmente antes de eliminar la fila principal.
fuente
Esto sucede porque la entidad secundaria está marcada como modificada en lugar de eliminada.
Y la modificación que EF hace a la entidad secundaria cuando
parent.Remove(child)
se ejecuta, es simplemente establecer la referencia a su padre ennull
.Puede verificar el EntityState del niño escribiendo el siguiente código en la Ventana Inmediata de Visual Studio cuando ocurra la excepción, después de ejecutar
SaveChanges()
:donde X debe ser reemplazado por la entidad eliminada.
Si no tiene acceso a la
ObjectContext
ejecución_context.ChildEntity.Remove(child)
, puede resolver este problema haciendo que la clave externa sea parte de la clave primaria en la tabla secundaria.De esta manera, si ejecuta
parent.Remove(child)
, EF marcará correctamente la entidad como eliminada.fuente
Este tipo de solución me sirvió:
Es importante decir que esto elimina todos los registros y los inserta nuevamente. Pero para mi caso (menos de 10) está bien.
Espero que ayude.
fuente
Me encontré con este problema hoy y quería compartir mi solución. En mi caso, la solución fue eliminar los elementos secundarios antes de obtener el elemento primario de la base de datos.
Anteriormente lo estaba haciendo como en el código a continuación. Entonces obtendré el mismo error listado en esta pregunta.
Lo que funcionó para mí es obtener primero los elementos secundarios, utilizando parentId (clave externa) y luego eliminar esos elementos. Entonces puedo obtener el padre de la base de datos y en ese punto, ya no debería tener ningún elemento secundario y puedo agregar nuevos elementos secundarios.
fuente
Debe borrar manualmente la colección ChildItems y agregar nuevos elementos en ella:
Después de eso, puede llamar al método de extensión DeleteOrphans que se manejará con entidades huérfanas (debe llamarse entre los métodos DetectChanges y SaveChanges).
fuente
context.DetectChanges();
.He probado estas soluciones y muchas otras, pero ninguna de ellas funcionó. Como esta es la primera respuesta en Google, agregaré mi solución aquí.
El método que funcionó bien para mí fue eliminar las relaciones de la imagen durante los commits, por lo que EF no tenía nada que arruinar. Hice esto volviendo a encontrar el objeto principal en el DBContext y eliminándolo. Dado que las propiedades de navegación del objeto re-encontrado son todas nulas, las relaciones de los niños se ignoran durante la confirmación.
Tenga en cuenta que esto supone que las claves externas están configuradas con ON DELETE CASCADE, por lo que cuando se elimina la fila principal, la base de datos limpiará los elementos secundarios.
fuente
Solía solución de Mosh , pero no era obvio para mí la forma de aplicar la clave composición correctamente en código por primera vez.
Entonces aquí está la solución:
fuente
Tuve el mismo problema, pero sabía que había funcionado bien en otros casos, así que reduje el problema a esto:
Todo lo que tenía que hacer era hacer que ParentId formara parte del PK compuesto para indicar que los niños no pueden existir sin un padre. Utilicé el modelo DB-first, agregué el PK y marqué la columna parentId como EntityKey (por lo tanto, tuve que actualizarlo tanto en DB como en EF, no estoy seguro de si EF sería suficiente).
Una vez que lo piensa, es una distinción muy elegante que EF usa para decidir si los niños "tienen sentido" sin un padre (en este caso, Clear () no los eliminará y lanzará una excepción a menos que establezca ParentId en algo más / especial ), o, como en la pregunta original, esperamos que los elementos se eliminen una vez que se eliminen del elemento primario.
fuente
Este problema surge porque tratamos de eliminar la tabla primaria y aún hay datos de la tabla secundaria. Resolvemos el problema con la ayuda de la eliminación en cascada.
En el modelo Crear método en la clase dbcontext.
Después de eso, en nuestra llamada API
La opción de eliminación en cascada elimina también la tabla secundaria relacionada con los padres con este código simple. Haz que lo intentes de esta manera simple.
Eliminar rango que se utiliza para eliminar la lista de registros en la base de datos Gracias
fuente
También resolví mi problema con la respuesta de Mosh y pensé en la respuesta de PeterB era un poco porque usaba una enumeración como clave foránea. Recuerde que deberá agregar una nueva migración después de agregar este código.
También puedo recomendar esta publicación de blog para otras soluciones:
http://www.kianryan.co.uk/2013/03/orphaned-child/
Código:
fuente
Usando la solución de Slauma, creé algunas funciones genéricas para ayudar a actualizar objetos secundarios y colecciones de objetos secundarios.
Todos mis objetos persistentes implementan esta interfaz
Con esto implementé estas dos funciones en mi repositorio
Para usarlo hago lo siguiente:
Espero que esto ayude
EXTRA: También puede hacer una clase separada DbContextExtentions (o su propio contexto de contexto):
y úsalo como:
fuente
Tuve el mismo problema cuando voy a eliminar mi registro que cuando ocurrió algún problema, porque esta solución de problema es que cuando va a eliminar su registro de lo que falta algo antes de eliminar el encabezado / registro maestro, debe escribir en el código para elimine sus detalles antes del encabezado / Master Espero que su problema se resuelva.
fuente
Me he encontrado con este problema antes de varias horas e intento todo, pero en mi caso la solución fue diferente de la que se menciona arriba.
Si usa una entidad ya recuperada de la base de datos e intenta modificarla para niños, se producirá el error, pero si obtiene una copia nueva de la entidad de la base de datos no debería haber ningún problema. No uses esto:
Utilizar este:
fuente