sql-server nhibernate session transactions nested

sql server - ¿Cómo hago transacciones anidadas en NHibernate?



sql-server session (4)

¿Puedo realizar transacciones anidadas en NHibernate y cómo puedo implementarlas? Estoy usando SQL Server 2008, por lo que el soporte está definitivamente en el DBMS.

Encuentro que si intento algo como esto:

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

luego, cuando se trata de outerTX.Commit() la transacción se ha vuelto inactiva y se traduce en una ObjectDisposedException en la sesión AdoTransaction.

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

Ahora estoy usando la implementación UnitOfWork de Ayende (gracias Sneal).

Perdone la ingenuidad en esta pregunta, todavía soy nuevo en NHibernate.

¡Gracias!

EDITAR : he descubierto que puede utilizar TransactionScope, 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 obliga a usar SQL Server, y también descubrí que si la base de datos es remota, debe preocuparse por tener habilitado el MSDTC ... un componente más para ir incorrecto. Las transacciones anidadas son tan útiles y fáciles de hacer en SQL que asumí que NHibernate tendría alguna forma de emular lo mismo ...


Como sugirió Satish, las transacciones anidadas no se admiten en NHibernate. No me he topado con escenarios donde se necesitaran transacciones anidadas, pero ciertamente enfrenté problemas en los que 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 una implementación de ejemplo para NHibernate, pero también debería funcionar para el servidor SQL: http://rajputyh.blogspot.com/2011/02/nested-transaction-handling-with.html


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

Acabo de reescribir nuestro UnitOfWork ayer por estas razones, originalmente estaba basado en el de Gabriel.

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

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


He estado luchando con esto por un tiempo ahora. Voy a tener otra grieta en eso.

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

Debido a que 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 solo, sin el contexto de una transacción más grande. En este caso, en CreateSample se creará y CreateSample una nueva transacción, o se retrotraerá si se produce una excepción.

En Test_NestedTransaction() , estamos creando una muestra y un proyecto. Si algo sale mal, queremos que ambos retrocedan. En realidad, el código en CreateSample y CreateProject se ejecutará como si no hubiera ninguna transacción; es completamente la transacción externa la que decide si deshacer o confirmar, y lo hace en función de si se produce una excepción. Realmente es por eso que estoy usando una transacción creada manualmente para la transacción externa; por lo tanto, tengo control sobre si se debe cometer o deshacer, en lugar de simplemente omitir a excepción-rollback-else-commit .

Usted podría lograr lo mismo sin Rhino.Commons poniendo un montón 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í. Pero With.Transaction , a pesar de la dificultad de crear delegados anónimos, lo hace de manera muy conveniente.

Una ventaja de este enfoque sobre el uso de TransactionScope s (aparte de la dependencia de MSDTC) es que debería haber un solo vaciado a la base de datos en la confirmación final de la transacción externa, independientemente de cuántos métodos se hayan llamado entre medias. En otras palabras, no necesitamos escribir datos no comprometidos en la base de datos a medida que avanzamos, siempre lo escribimos en el caché local de NHibernate.

En resumen, esta solución no ofrece un control definitivo sobre sus transacciones, ya que nunca usa más de una transacción. Supongo que puedo aceptar eso, ya que las transacciones anidadas de ninguna manera son universalmente compatibles en todos los DBMS. Pero ahora quizás al menos pueda escribir código sin preocuparme por si ya estamos en una transacción o no.


Las sesiones de NHibernate no admiten transacciones anidadas.

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

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

Debe envolverlo en un TransactionScope para admitir transacciones anidadas.

MSDTC debe estar habilitado o obtendrá un 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."}