2009-07-28 21 views
17

¿Puedo hacer transacciones anidadas en NHibernate y cómo las implemento? Estoy usando SQL Server 2008, por lo que el soporte está definitivamente en el DBMS.¿Cómo hago transacciones anidadas en NHibernate?

Me parece que si lo intento algo como esto:

using (var outerTX = UnitOfWork.Current.BeginTransaction()) 
{ 
    using (var nestedTX = UnitOfWork.Current.BeginTransaction()) 
    { 
     ... do stuff 
     nestedTX.Commit(); 
    } 

    outerTX.Commit(); 
} 

luego por el momento se trata de outerTX.Commit() la transacción se ha vuelto inactiva, y resulta en una ObjectDisposedException en el AdoTransaction sesión.

¿Se supone que debemos crear sesiones anidadas de NHibernate en su lugar? ¿O hay alguna otra clase que deberíamos utilizar para envolver las transacciones (he oído hablar de TransactionScope, pero no estoy seguro de qué es eso)?

Ahora estoy usando Ayende's UnitOfWork implementation (gracias Sneal).

Perdón cualquier ingenuidad en esta pregunta, todavía soy nuevo en NHibernate.

Gracias!

EDITAR: He descubierto que se puede utilizar TransactionScope, tales como:

using (var transactionScope = new TransactionScope()) 
{ 
    using (var tx = UnitOfWork.Current.BeginTransaction()) 
    { 
     ... do stuff 
     tx.Commit(); 
    } 

    using (var tx = UnitOfWork.Current.BeginTransaction()) 
    { 
     ... do stuff 
     tx.Commit(); 
    } 

    transactionScope.Commit(); 
} 

Sin embargo, no estoy tan entusiasmado con esto, ya que nos encierra en que el uso de SQL Server y También encontré que si la base de datos es remota, entonces debe preocuparse por tener MSDTC habilitado ... un componente más que no funciona. Las transacciones anidadas son tan útiles y fáciles de hacer en SQL que supongo que NHibernate tendría alguna forma de emular el mismo ...

+1

¿Ha sido capaz de encontrar la respuesta? ¿Cómo hacer transacciones anidadas finalmente? – learning

+0

@ user281180, más o menos. No he encontrado la forma de hacerlo, pero puedes aproximar la experiencia. Han blogueado sobre esto aquí: http://blog.constructionhive.com/2010/07/22/nested-transactions-and-nhibernate/ – Gavin

Respuesta

1

Esa implementación no admite el anidamiento, si desea anidar use Ayende's UnitOfWork implementation. Otro problema con la implementación que está utilizando (al menos para las aplicaciones web) es que se mantiene en la instancia de ISession en una variable estática.

Ayer reescribí nuestro UnitOfWork por estos motivos, originalmente se basó en el de Gabriel.

No usamos UnitOfWork.Current.BeginTransaction(), usamos UnitofWork.TransactionalFlush(), que crea una transacción por separado al final para enjuagar todos los cambios a la vez.

using (var uow = UnitOfWork.Start()) 
{ 
    var entity = repository.Get(1); 
    entity.Name = "Sneal"; 
    uow.TransactionalFlush(); 
} 
+0

Sí, creo que debería haber usado Rhino Commons desde el principio ... Creo Fui con Gabriel para poder tener una idea de cómo funcionaba el patrón de unidad de trabajo construyéndolo desde cero (y realmente me ayudó a entender mucho), pero tal vez ahora es el momento de jugar con los grandes ... gracias . – Gavin

+0

@Sneal: Implementamos UnitOfWork de Ayende utilizando Rhino.Commons, y está bien, pero todavía no tengo claro cómo hacer funcionar las transacciones anidadas.El código que describí en mi pregunta original actúa exactamente de la misma manera que antes (es decir, los objetos de la transacción parecen compartidos). ¿Me puede dar algunos consejos? Gracias. – Gavin

2

He estado luchando con esto desde hace un tiempo. Voy a tener otra oportunidad.

Quiero implementar transacciones en contenedores de servicio individuales, porque eso los hace autónomos, pero luego ser capaz de anidar varios de esos métodos de servicio dentro de una transacción más grande y revertir todo el lote si es necesario.

Porque estoy usando Rhino Commons Ahora voy a intentar refactorizar usando el método With.Transaction. Básicamente, nos permite escribir código como si las transacciones estuvieran anidadas, aunque en realidad solo hay una.

Por ejemplo:

private Project CreateProject(string name) 
{ 
    var project = new Project(name); 
    With.Transaction(delegate 
    { 
     UnitOfWork.CurrentSession.Save(project); 
    }); 
    return project; 
} 

private Sample CreateSample(Project project, string code) 
{ 
    var sample = new Sample(project, code); 
    With.Transaction(delegate 
    { 
     UnitOfWork.CurrentSession.Save(sample); 
    }); 
    return sample; 
} 

private void Test_NoNestedTransaction() 
{ 
    var project = CreateProject("Project 1"); 
} 

private void TestNestedTransaction() 
{ 
    using (var tx = UnitOfWork.Current.BeginTransaction()) 
    { 
     try 
     { 
      var project = CreateProject("Project 6"); 
      var sample = CreateSample(project, "SAMPLE006", true); 
     } 
     catch 
     { 
      tx.Rollback(); 
      throw; 
     } 
     tx.Commit(); 
    } 
} 

En Test_NoNestedTransaction(), estamos creando un proyecto en solitario, sin el contexto de una transacción más grande. En este caso, en CreateSample se creará y se comprometerá una nueva transacción, o se retrotraerá si ocurre una excepción.

En Test_NestedTransaction(), estamos creando tanto una muestra como un proyecto. Si algo sale mal, queremos que ambos sean revertidos. En realidad, el código en CreateSample y CreateProject se ejecutará como si no hubiera transacciones en absoluto; es totalmente la transacción externa la que decide si revertir o comprometer, y lo hace en función de si se lanza una excepción. Realmente es por eso que estoy usando una transacción creada manualmente para la transacción externa; así que tenemos control sobre si comprometer o revertir, en lugar de simplemente establecer el valor predeterminado en on-exception-rollback-else-commit.

que podría lograr lo mismo sin Rhino.Commons poniendo una gran cantidad de este tipo de cosas a través de su código:

if (!UnitOfWork.Current.IsInActiveTransaction) 
{ 
    tx = UnitOfWork.Current.BeginTransaction(); 
} 

_auditRepository.SaveNew(auditEvent); 
if (tx != null) 
{ 
    tx.Commit(); 
} 

... y así sucesivamente. Pero With.Transaction, a pesar de la torpeza de la necesidad de crear delegados anónimos, lo hace de manera bastante conveniente.

Una ventaja de este enfoque sobre el uso de TransactionScope s (aparte de la confianza en MSDTC) es que debe haber un solo enrojecimiento en la base de datos en la confirmación final de transacción externa, independientemente de cuántos métodos se hayan llamado entre. En otras palabras, no necesitamos escribir datos no confirmados en la base de datos sobre la marcha, siempre estamos simplemente escribiéndolo en el caché NHibernate local.

En resumen, esta solución no ofrece el máximo control sobre sus transacciones, ya que nunca utiliza más de una transacción. Creo que puedo aceptar eso, ya que las transacciones anidadas de ninguna manera son universalmente compatibles en todos los DBMS de todos modos. Pero ahora, tal vez, al menos pueda escribir código sin preocuparme si ya estamos en una transacción o no.

13

Las sesiones de NHibernate no admiten transacciones anidadas.

La siguiente prueba es siempre verdad en la versión 2.1.2:

var session = sessionFactory.Open(); 
var tx1 = session.BeginTransaction(); 
var tx2 = session.BeginTransaction(); 
Assert.AreEqual(tx1, tx2); 

Necesitas envolverlo en un TransactionScope para apoyar las transacciones anidadas.

MSDTC debe estar habilitado o se obtendrá el error:

{"Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool."}

+0

¿Significa esto que, incluso si creo transacciones anidadas en NHibernate, esas serán tratadas como una conexión más externa? –

2

Como sugirió Satish, transacciones anidadas no se admiten en NHibernate. No he encontrado escenarios donde se necesitaban transacciones anidadas, pero ciertamente he enfrentado problemas donde tuve que ignorar la creación de transacciones si otras ya estaban activas en otras unidades de trabajo.

El enlace del blog a continuación proporciona un ejemplo de implementación de NHibernate, pero debería funcionar también para el servidor SQL: http://rajputyh.blogspot.com/2011/02/nested-transaction-handling-with.html

Cuestiones relacionadas