¿Cómo eliminar un objeto hijo en NHibernate?

79

Tengo un objeto principal que tiene una relación de uno a muchos con una IList de objetos secundarios. ¿Cuál es la mejor forma de eliminar los objetos secundarios? No eliminaré al padre. Mi objeto principal contiene una IList de objetos secundarios. Aquí está el mapeo de la relación uno a muchos:

<bag name="Tiers" cascade="all">
  <key column="mismatch_id_no" />
  <one-to-many class="TGR_BL.PromoTier,TGR_BL"/>
</bag>

Si intento eliminar todos los objetos de la colección usando clear (), luego llamo a SaveOrUpdate (), obtengo esta excepción:

System.Data.SqlClient.SqlException: Cannot insert the value NULL into column

Si trato de eliminar los objetos secundarios individualmente y luego los elimino del padre, obtengo una excepción:

deleted object would be re-saved by cascade

Esta es la primera vez que trato de eliminar objetos secundarios en NHibernate. ¿Qué estoy haciendo mal?

editar: Solo para aclarar: NO estoy tratando de eliminar el objeto principal, solo los objetos secundarios. Tengo la relación establecida como uno a muchos en el padre. ¿También necesito crear una relación de varios a uno en el mapeo de objetos secundarios?

Mark Struzinski
fuente

Respuestas:

139

Está recibiendo el primer error porque, cuando elimina los elementos de la colección, el modo de operación predeterminado de NHibernate es simplemente romper la asociación. En la base de datos, NHibernate intenta establecer la columna de clave externa en la fila secundaria en nula. Dado que no permite valores nulos en esa columna, SQL Server genera el error. Borrar la colección no necesariamente eliminará el objeto hijo, pero una forma de hacerlo es establecer cascade = all-delete-orphan. Esto informa a NHibernate que debe eliminar las filas recién huérfanas en lugar de configurar la columna de clave externa.

Obtiene el segundo error porque cuando llama a SaveOrUpdate, NHibernate primero elimina todos los objetos secundarios. Entonces, debido a que ninguna relación está marcada como inversa, NHibernate también intenta establecer la columna de clave externa en su tabla secundaria en nula. Dado que las filas ya se han eliminado, recibe el segundo error. Debe establecer inverso = verdadero en un lado de su relación para solucionar este problema. Esto generalmente se hace en el lado uno (clave principal o principal). Si no hace esto, NHibernate hará las actualizaciones apropiadas para cada lado de la relación. Desafortunadamente, ejecutar dos actualizaciones no es lo apropiado.

Siempre debes marcar un lado de tus relaciones como el lado inverso. Dependiendo de cómo codifique, es posible que necesite o no usar la cascada. Si desea aprovechar las eliminaciones de un disparo como está tratando de hacer con Clear (), debe definir su cascada.

Arrojar
fuente
Solo para aclarar: solo tengo la relación definida en el objeto Padre en este momento. Intenté establecer la cascada en ese objeto en "todos" y "todos-eliminar-huérfanos" con los mismos resultados en ambas ocasiones. ¿Quiere decir que también necesito definir una relación de varios a uno en el objeto secundario?
Mark Struzinski
Y en este caso, ¿el inverso = "verdadero" pertenece al objeto padre o hijo?
Mark Struzinski
3
pertenece a la clase que no contiene la clave externa ... generalmente la principal.
pmlarocque
Gracias por esta explicación, nuevamente un problema inverso me atrapó, y verlo explicado así realmente ayudó.
Mark Dickinson
Esto realmente me ayudó a resolver un problema que estaba teniendo. Gracias.
Robin Robinson
3

De acuerdo con la respuesta de Chuck, resolví mi problema agregando Inverse = true en el mapeo del lado principal:

El mensaje tiene muchos mensajes enviados a:

[HasMany(typeof(MessageSentTo), Cascade = ManyRelationCascadeEnum.AllDeleteOrphan, Inverse = true)]
public IList<MessageSentTo> MessageSendTos
{
    get { return m_MessageSendTo; }
    set { m_MessageSendTo = value; }
}

Estoy usando Castle ActiveRecord. Gracias Chuck.

hanuman0503
fuente
2

Intente usar merge () en lugar de saveOrUpdate (). Además, asegúrese de que su cascada esté configurada en all-delete-huérfano y que su relación padre-hijo sea invertible (inverso = verdadero en el padre y luego un campo en el hijo que es la identificación del padre con not-null = verdadero) .

Elie
fuente
2

En nuestro ejemplo, tenemos categorías con muchos productos en los que un producto no admite nulos.

Puede solucionar el problema eliminando el producto y eliminándolo de la colección principal antes de la descarga, pero todavía estamos buscando una mejor solución para esto.

product = pRepo.GetByID(newProduct.ProductID);
product.Category.Products.Remove(product);
pRepo.Delete(product);

Espero que ayude de todos modos

Liath
fuente
1
Sin embargo, esto requiere otro repositorio.
UpTheCreek
0

Cambie el valor del atributo en cascada de "todos" a "todos-eliminar-huérfanos".

Thomas Tekavec
fuente
Probé esto. Sigo recibiendo la misma excepción.
Mark Struzinski
16
all-delete-orphan elimina a los hijos cuando elimina al padre. no tiene nada que ver con eliminar a los niños como pregunta este tipo.
Kyle West
-3

establezca Not-Null = true en su mapeo en la columna que causa el problema. Sin embargo, no estoy seguro de la sintaxis exacta (lo siento).

Kyle West
fuente
Ok, configuré la columna en not-null = "true" en el mapeo del objeto secundario. Entonces, llamo al método clear () en la IList de objetos secundarios, luego pruebo SaveOrUpdate (). Sigo recibiendo el error "No se puede insertar el valor NULL en la columna". ¿Estoy haciendo algo más mal?
Mark Struzinski
¿En qué columna se lanza el error? ¿Cómo está eliminando exactamente a los niños en su modelo?
Kyle West
Estoy llamando al método clear () en el objeto secundario Ilist en mi objeto principal. Luego llamo a SaveOrUpdate () en el padre.
Mark Struzinski