2009-11-19 28 views
5

Estoy tratando de utilizar NHibernate con entidades heredadas que no están mapeadas con NHibernate. En ocasiones, esto significa que debo lavar manualmente los datos de NHibernate a la base de datos para no recibir excepciones de clave externa cuando intento conectar las entidades heredadas con entidades asignadas a NHibernate.Limpiar NHibernate al tiempo que permite la reversión de transacción

Ocurre un problema cuando esto tiene lugar dentro de una transacción que luego debe revertirse. Los datos eliminados de NHibernate no se revierten.

¿Hay algo que pueda hacer al respecto?

ACTUALIZACIÓN

Aún curioso cómo hacer esto - no creo que ninguna de las respuestas dadas abordar la cuestión. Necesito llamar a Flush(). La pregunta es, ¿cómo revertir los datos que se han limpiado?

+0

Háganme saber por qué baja la votación? Las respuestas actuales son buenas, pero no resuelven el problema. Necesito poder ir a la base de datos para que las consultas administradas que no son de NHibernate puedan acceder a los datos que se han actualizado en un NHibernate. Si no puedo llamar al color, ¿de qué otro modo pueden las consultas administradas que no son de NHibernate acceder a los datos? Imagínese, haciendo esto usted mismo con simples llamadas System.Data.SqlClient, sería bastante simple: envuelva un montón de llamadas SqlCommand.ExecuteUpdate() en un TransactionScope y eso es todo. Puede anidar transacciones, retrotraerlas, etc. y todo se comportaría como se esperaba. – cbp

Respuesta

2

Al usar transacciones con NHibernate intente evitar el uso de Session.Flush() y en su lugar use transaction.Commit() que llama session.flush() internamente.

Si durante el Commit() se produce un error y la transacción necesita deshacerse, esto puede abordarse de esta manera.

public static void CommitChanges() 
{ 
    ITransaction transaction = Session.BeginTransaction(); 

    try 
    { 
     transaction.Commit(); 
    } 
    catch (HibernateException ex) 
    { 
     transaction.Rollback(); 
     //close and dispose session here 
     throw ex; 
    } 
    finally 
    { 
     transaction.Dispose(); 
    } 
} 

Ahora, si una llamada manual para flush() o una llamada a commit() pasa a través de éxito no es una forma de deshacer la transacción utilizando mecanismos de NHibernate. Especialmente cuando se llama al comando transaction.Commit(), la AdoTransaction creada por NHibernate se elimina justo después de que termine Commit(), por lo que no puede acceder a ella para poder retroceder.

El ejemplo de código anterior le permite detectar los errores que se producen durante la confirmación y luego retrotraer la transacción que ya comenzó.

Ahora, en lugar de llamar a transaction.Commit() en el ejemplo anterior, llame a la sesión.Flush() en mis pruebas no se guardan datos en la base de datos ya que la transacción nunca se confirma.

No tengo ni idea de cómo se ve el código, pero si está llamando en un patrón, como muestra el ejemplo del código anterior, el transaction.commit() en lugar del Session.Flush() debería darle una forma para lograr lo que quieres

+0

Su sección de captura (...) no agrega nada funcional, ya que el Dispose() ya realiza un Rollback (si no está comprometido). – tofi9

+0

@taoufik Temo que tu comentario no sea válido. El comando Dispose() de la interfaz ITransaction implementada por el objeto AdoTransaction en el código NHibernate es responsable solo de liberar los recursos administrados y no administrados. Además, un vistazo rápido al código fuente de NHibernate demostró el hecho de que el Dispose() NO llama al ITransaction.RollBack() ni al método RollBack() subyacente del objeto IDbTransaction ... Por lo tanto, la sección catch (...) es completamente funcional y necesario para la muestra de código anterior. – tolism7

+0

La siguiente publicación muestra un fragmento de código (de Reflector) que demuestra que Dispose llama a Rollback: http: // stackoverflow.com/questions/641660/will-a-using-statement-rollback-a-database-transaction-if-an-error-happens – tofi9

3

Por el formato, me permito actualizar la respuesta de tolism7 aquí.

  1. uso using y olvidarse de transaction.Dispose() - la transaction serán automáticamente Dispose 'd de al final del uso de bloque.
  2. throw - no tirar ex porque significa tirar su StackTrace (ver this post donde dice "Cuando el .NET Framework ejecuta esta sentencia:. throw ex; se tira a la basura toda la información de la pila por encima de la función actual")

.

public void CommitChanges() 
{ 
    using (var transaction = Session.BeginTransaction()) // <-- open scope 
     try 
     { 
      // do something 
      transaction.Commit(); 
     } 
     catch (HibernateException) 
     { 
      transaction.Rollback(); 
      _session.Close(); 
      _session.Dispose(); 

      throw; // <-- this way the stacktrace stays intact! 
     } 
} 

A VB.NET version de esta pieza de código se puede encontrar here.

4

cheque esto: Force query execution without flush/commit

parecía que tenía el mismo problema, me gustaría eliminar y luego me gustaría deshacer pero algunos datos permanecería persistido en la base de datos. Sin embargo, había algunas partes en mi código que llamarían una confirmación, que no se puede revertir. Considere el fragmento de código de las respuestas aceptadas como el uso correcto de transacciones, vaciados, reversiones y confirmaciones y tenga en cuenta que este patrón se puede extender ...

en una sola unidad de trabajo (es decir, consideramos una Solicitud en un aplicación web como una sola unidad de trabajo y todo lo que sucede en esa solicitud existe en una sola transacción, que está comprometida onEndRequest):

  1. que llaman _sessionFactory.OpenSession(), _session.BeginTransaction(), _session.CommitTransaction() y _session.CloseSession() sólo una vez.

  2. puede llamar a _session.Flush() y _session.RollBackTransaction() tantas veces como desee, pero se solicita automáticamente que Flush() ejecute Commit automáticamente. Es posible que desee llamar a Flush cuando necesite realizar una consulta y asegurarse de que los datos obtenidos no estén obsoletos.

  3. Tenga en cuenta que una vez que se compromete una transacción de confirmación, todas las operaciones siguientes no se producen en esa transacción. En lugar NHibernate creará la transacción necesaria bajo el capó (http://www.nhprof.com/Learn/Alerts/DoNotUseImplicitTransactions) en el que el punto ya tiene problemas de seguimiento de la consistencia e integridad posiblemente lógica

  4. Si realmente tiene que llamar a cometer en el medio de su unidad de trabajo que es muy recomendable para crear una nueva transacción en ese punto para que pueda administrarla explícitamente

  5. Lo que es aún mejor es probar Transacciones anidadas supuestamente permitirá confirmaciones parciales; puede deshacer la transacción "raíz" y todos los cambios se revertirán. Realmente no he probado esta característica de .NET y SQL Server, aunque la transacción anidada en la base de datos deja mucho que desear y no sé exactamente cómo ADO.NET incorpora esta característica.

puntos 1 a 4 han sido probados con todas las versiones de NHibernate a partir de 1.2.

+0

Gracias por su comentario. En su prueba, ¿observó que una vez que los datos han sido eliminados, a menudo no es posible deshacer la transacción? Esto es lo que me está causando el dolor de cabeza: a veces necesita enjuagar a mitad de camino a través de una transacción, pero también necesita poder reiniciar la transacción más adelante. Esto no parece ser posible con NHibernate. – cbp

+0

Si se han purgado, son perfectamente reversibles. Tienes que perfilar tus operaciones de DB, tal vez algo está llamando/provocando una confirmación. He estado haciendo vaciados explícitos e implícitos (cuando haces una consulta hql/criteria por ejemplo) y haciendo rollback (en excepciones o para probar) al final de la transacción sin problemas. – Jaguar

Cuestiones relacionadas